diff options
Diffstat (limited to 'packages/website')
43 files changed, 1580 insertions, 162 deletions
diff --git a/packages/website/md/docs/deployer/installation.md b/packages/website/md/docs/deployer/installation.md new file mode 100644 index 000000000..c02dbadc6 --- /dev/null +++ b/packages/website/md/docs/deployer/installation.md @@ -0,0 +1,24 @@ +#### CLI Installation + +```bash +yarn global add @0xproject/deployer +``` + +#### API Installation + +```bash +yarn add @0xproject/deployer +``` + +**Import** + +```typescript +import { Deployer, Compiler } from '@0xproject/deployer'; +``` + +or + +```javascript +var Deployer = require('@0xproject/deployer').Deployer; +var Compiler = require('@0xproject/deployer').Compiler; +``` diff --git a/packages/website/md/docs/deployer/introduction.md b/packages/website/md/docs/deployer/introduction.md new file mode 100644 index 000000000..7ebd26a3c --- /dev/null +++ b/packages/website/md/docs/deployer/introduction.md @@ -0,0 +1,18 @@ +Welcome to the [Deployer](https://github.com/0xProject/0x-monorepo/tree/development/packages/deployer) documentation! Deployer is a tool for compiling and deploying Solidity smart contracts with ease. + +It serves a similar purpose as parts of the [Truffle framework](http://truffleframework.com/), but with the UNIX philosophy in mind: Make each program do one thing well. This tool is for intermediate to advanced Solidity developers that require greater configurability and reliability. + +Deployer has the following advantages over Truffle: + +* Deploy each smart contract with a specific version of Solidity. +* Improved artifact files: + * Properly segregated artifacts to support storing different versions of smart contract deployed on different networks. + * Storage of constructor args, source maps and paths to all requisite source files. + * An easy to maintain codebase: TypeScript + Single repo. + * Allows you to specify the deployer RPC address. + * Supports Solidity version ranges - contract compiled with latest Solidity version that satisfies the range. + * Migrations that work with `async/await`. + * Migrations that can be written synchronously in order to guarentee deterministic contract addresses. + * No race conditions when running migrations. + +Deployer can be used as a command-line tool or as an imported module. diff --git a/packages/website/md/docs/deployer/usage.md b/packages/website/md/docs/deployer/usage.md new file mode 100644 index 000000000..295af55e1 --- /dev/null +++ b/packages/website/md/docs/deployer/usage.md @@ -0,0 +1,56 @@ +#### CLI Usage + +```bash +$ 0x-deployer --help +0x-deployer [command] + +Commands: + 0x-deployer compile compile contracts + 0x-deployer deploy deploy a single contract with provided arguments + +Options: + --version Show version number [boolean] + --contracts-dir path of contracts directory to compile [string] [default: + "/path/to/contracts"] + --network-id mainnet=1, kovan=42, testrpc=50 [number] [default: 50] + --should-optimize enable optimizer [boolean] [default: false] + --artifacts-dir path to write contracts artifacts to [string] [default: + "/path/to/artifacts"] + --jsonrpc-port port connected to JSON RPC [number] [default: 8545] + --gas-price gasPrice to be used for transactions + [string] [default: "2000000000"] + --account account to use for deploying contracts [string] + --contracts comma separated list of contracts to compile + [string] [default: "*"] + --help Show help [boolean] +``` + +#### API Usage + +##### Migrations + +You can write migration scripts (similar to `truffle migrate`), that deploys multiple contracts and configures them. Below you'll find a simple example of such a script to help you get started. + +```typescript +import { Deployer } from '@0xproject/deployer'; +import * as path from 'path'; + +const deployerOpts = { + artifactsDir: path.resolve('src', 'artifacts'), + jsonrpcUrl: 'http://localhost:8545', + networkId: 50, + defaults: { + gas: 1000000, + }, +}; + +const deployer = new Deployer(deployerOpts); + +(async () => { + const etherToken = await deployer.deployAndSaveAsync('WETH9'); +})().catch(console.log); +``` + +**Tip:** Be sure to start an Ethereum node at the supplied `jsonrpcUrl`. We recommend testing with [Ganache-cli](https://github.com/trufflesuite/ganache-cli) + +A more sophisticated example can be found [here](https://github.com/0xProject/0x-monorepo/tree/development/packages/contracts/migrations) diff --git a/packages/website/md/docs/json_schemas/installation.md b/packages/website/md/docs/json_schemas/installation.md new file mode 100644 index 000000000..50a37bae1 --- /dev/null +++ b/packages/website/md/docs/json_schemas/installation.md @@ -0,0 +1,17 @@ +**Install** + +```bash +yarn add @0xproject/json-schemas +``` + +**Import** + +```javascript +import { schemas } from '@0xproject/json-schemas'; +``` + +or + +```javascript +var schemas = require('@0xproject/json-schemas').schemas; +``` diff --git a/packages/website/md/docs/json_schemas/introduction.md b/packages/website/md/docs/json_schemas/introduction.md new file mode 100644 index 000000000..a27f4b521 --- /dev/null +++ b/packages/website/md/docs/json_schemas/introduction.md @@ -0,0 +1,3 @@ +Welcome to the [@0xproject/json-schemas](https://github.com/0xProject/0x-monorepo/tree/development/packages/json-schemas) documentation! This package provides JSON schemas for validating 0x Protocol & Standard Relayer API data structures. It provides both the raw JSON schemas and a schema validator class to interact with them from a JS project. + +If you are not using a Javascript-based language for your project, you can copy-paste the JSON schemas within this package and use them together with a [JSON Schema](http://json-schema.org/) implementation in your [language of choice](http://json-schema.org/implementations.html) (e.g Python, Haskell, Go, C, C++, Rust, Ruby, Scala, etc...). diff --git a/packages/website/md/docs/json_schemas/schemas.md b/packages/website/md/docs/json_schemas/schemas.md new file mode 100644 index 000000000..fcf5d8df6 --- /dev/null +++ b/packages/website/md/docs/json_schemas/schemas.md @@ -0,0 +1,28 @@ +0x Protocol Schemas + +* [Basic types](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/basic_type_schemas.ts) (e.g Ethereum address, number) +* [ECSignature](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/ec_signature_schema.ts) +* [Order/SignedOrder](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/order_schemas.ts) +* [OrderHash](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/order_hash_schema.ts) + +0x.js Schemas + +* [BlockRange](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/block_range_schema.ts) +* [IndexFilter Values](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/index_filter_values_schema.ts) +* [OrderFillRequests](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/order_fill_requests_schema.ts) +* [OrderCancellationRequests](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/order_cancel_schema.ts) +* [OrderFillOrKillRequests](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts) +* [SignedOrders](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/signed_orders_schema.ts) +* [Token](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/token_schema.ts) +* [TxData](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/tx_data_schema.ts) + +Standard Relayer API Schemas + +* [Error response](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_error_response_schema.ts) +* [Fees payload](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_fees_payload_schema.ts) +* [Fees response](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_fees_response_schema.ts) +* [Orderbook channel subscribe](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts) +* [Orderbook channel snapshot](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts) +* [Orderbook channel update](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts) +* [Orderbook response](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts) +* [Token pairs response](https://github.com/0xProject/0x-monorepo/blob/d4c1b3b0bd26e730ce6687469cdf7283877543e1/packages/json-schemas/schemas/relayer_api_token_pairs_response_schema.ts) diff --git a/packages/website/md/docs/json_schemas/usage.md b/packages/website/md/docs/json_schemas/usage.md new file mode 100644 index 000000000..68b801153 --- /dev/null +++ b/packages/website/md/docs/json_schemas/usage.md @@ -0,0 +1,14 @@ +The following example shows you how to validate a 0x order using the `@0xproject/json-schemas` package. + +```javascript +import {SchemaValidator, ValidatorResult, schemas} from '@0xproject/json-schemas'; + +const {orderSchema} = schemas; +const validator = new SchemaValidator(); + +const order = { + ... +}; +const validatorResult: ValidatorResult = validator.validate(order, orderSchema); // Contains all errors +const isValid: boolean = validator.isValid(order, orderSchema); // Only returns boolean +``` diff --git a/packages/website/md/docs/sol_cov/installation.md b/packages/website/md/docs/sol_cov/installation.md new file mode 100644 index 000000000..b9ce25a5f --- /dev/null +++ b/packages/website/md/docs/sol_cov/installation.md @@ -0,0 +1,17 @@ +**Install** + +```bash +yarn add @0xproject/sol-cov +``` + +**Import** + +```javascript +import { CoverageSubprovider } from '@0xproject/sol-cov'; +``` + +or + +```javascript +var CoverageSubprovider = require('@0xproject/sol-cov').CoverageSubprovider; +``` diff --git a/packages/website/md/docs/sol_cov/introduction.md b/packages/website/md/docs/sol_cov/introduction.md new file mode 100644 index 000000000..7064a3554 --- /dev/null +++ b/packages/website/md/docs/sol_cov/introduction.md @@ -0,0 +1 @@ +Welcome to the [@0xproject/sol-cov](https://github.com/0xProject/0x-monorepo/tree/development/packages/sol-cov) documentation! Sol-cov is a Solidity coverage tool for your smart contract tests. diff --git a/packages/website/md/docs/sol_cov/usage.md b/packages/website/md/docs/sol_cov/usage.md new file mode 100644 index 000000000..ea1982d97 --- /dev/null +++ b/packages/website/md/docs/sol_cov/usage.md @@ -0,0 +1,26 @@ +Sol-cov uses transaction traces in order to figure out which lines of Solidity source code have been covered by your tests. In order for it to gather these traces, you must add the `CoverageSubprovider` to the [ProviderEngine](https://github.com/MetaMask/provider-engine) instance you use when running your Solidity tests. If you're unfamiliar with ProviderEngine, please read the [Web3 Provider explained](https://0xproject.com/wiki#Web3-Provider-Explained) wiki article. + +The CoverageSubprovider eavesdrops on the `eth_sendTransaction` and `eth_call` RPC calls and collects traces after each call using `debug_traceTransaction`. `eth_call`'s' don't generate traces - so we take a snapshot, re-submit it as a transaction, get the trace and then revert the snapshot. + +```typescript +import { CoverageSubprovider } from '@0xproject/sol-cov'; + +const provider = new ProviderEngine(); + +const artifactsPath = 'src/artifacts'; +const contractsPath = 'src/contracts'; +const networkId = 50; +// Some calls might not have `from` address specified. Nevertheless - transactions need to be submitted from an address with at least some funds. defaultFromAddress is the address that will be used to submit those calls as transactions from. +const defaultFromAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; +const coverageSubprovider = new CoverageSubprovider(artifactsPath, contractsPath, networkId, defaultFromAddress); + +provider.addProvider(coverageSubprovider); +``` + +After your test suite is complete (e.g global `after` hook), you'll need to call: + +```typescript +await coverageSubprovider.writeCoverageAsync(); +``` + +This will create a `coverage.json` file in the `coverage` directory. This file has an [Istanbul format](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md) - so you can use any of the existing Instanbul reporters. diff --git a/packages/website/md/docs/subproviders/installation.md b/packages/website/md/docs/subproviders/installation.md new file mode 100644 index 000000000..a049ff0ec --- /dev/null +++ b/packages/website/md/docs/subproviders/installation.md @@ -0,0 +1,15 @@ +```bash +npm install @0xproject/subproviders --save +``` + +**Import** + +```typescript +import { LedgerSubprovider } from '@0xproject/subproviders'; +``` + +or + +```javascript +var LedgerSubprovider = require('@0xproject/subproviders').LedgerSubprovider; +``` diff --git a/packages/website/md/docs/subproviders/introduction.md b/packages/website/md/docs/subproviders/introduction.md new file mode 100644 index 000000000..835201064 --- /dev/null +++ b/packages/website/md/docs/subproviders/introduction.md @@ -0,0 +1 @@ +Welcome to the [Subproviders](https://github.com/0xProject/0x-monorepo/tree/development/packages/subproviders) documentation! Subproviders is a package containing useful [subproviders](https://0xproject.com/wiki#Web3-Provider-Explained) that can be used with the [Web3 Provider Engine](https://github.com/MetaMask/provider-engine) library. diff --git a/packages/website/md/docs/subproviders/ledger_node_hid.md b/packages/website/md/docs/subproviders/ledger_node_hid.md new file mode 100644 index 000000000..3089817af --- /dev/null +++ b/packages/website/md/docs/subproviders/ledger_node_hid.md @@ -0,0 +1,17 @@ +By default, node-hid transport support is an optional dependency. This is due to the requirement of native usb developer packages on the host system. If these aren't installed the entire `npm install` fails. We also no longer export node-hid transport client factories. To re-create this see our integration tests or follow the example below: + +```typescript +import Eth from '@ledgerhq/hw-app-eth'; +import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'; +async function ledgerEthereumNodeJsClientFactoryAsync(): Promise<LedgerEthereumClient> { + const ledgerConnection = await TransportNodeHid.create(); + const ledgerEthClient = new Eth(ledgerConnection); + return ledgerEthClient; +} + +// Create a LedgerSubprovider with the node-hid transport +ledgerSubprovider = new LedgerSubprovider({ + networkId, + ledgerEthereumClientFactoryAsync: ledgerEthereumNodeJsClientFactoryAsync, +}); +``` diff --git a/packages/website/md/docs/web3_wrapper/installation.md b/packages/website/md/docs/web3_wrapper/installation.md new file mode 100644 index 000000000..92794d9b0 --- /dev/null +++ b/packages/website/md/docs/web3_wrapper/installation.md @@ -0,0 +1,25 @@ +**Install** + +```bash +npm install @0xproject/web3-wrapper --save +``` + +**Import** + +```javascript +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +``` + +or + +```javascript +var Web3Wrapper = require('@0xproject/web3-wrapper').Web3Wrapper; +``` + +If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: + +``` +"include": [ + "./node_modules/web3-typescript-typings/index.d.ts", +] +``` diff --git a/packages/website/md/docs/web3_wrapper/introduction.md b/packages/website/md/docs/web3_wrapper/introduction.md new file mode 100644 index 000000000..ea2f4cf0d --- /dev/null +++ b/packages/website/md/docs/web3_wrapper/introduction.md @@ -0,0 +1 @@ +Welcome to the [Web3Wrapper](https://github.com/0xProject/0x-monorepo/tree/development/packages/web3-wrapper) documentation! Web3Wrapper is a convenience library that wraps Web3 v0.x, providing promise-based endpoints and a consistent API. diff --git a/packages/website/public/images/doc_icons/docs.png b/packages/website/public/images/doc_icons/docs.png Binary files differnew file mode 100644 index 000000000..17df04310 --- /dev/null +++ b/packages/website/public/images/doc_icons/docs.png diff --git a/packages/website/translations/chinese.json b/packages/website/translations/chinese.json index f5f906d6c..f610bf56c 100644 --- a/packages/website/translations/chinese.json +++ b/packages/website/translations/chinese.json @@ -56,11 +56,16 @@ "ABOUT": "关于我们", "CAREERS": "人才招聘", "CONTACT": "联系方式", + "DEPLOYER": "Deployer", + "JSON_SCHEMAS": "JSON Schemas", + "SOL_COV": "Solidity Coverage", + "SUBPROVIDERS": "Subproviders", "BLOG": "博客", "FORUM": "论坛", "CONNECT": "0x 连接", "WHITEPAPER": "白皮书", "WIKI": "维基", + "WEB3_WRAPPER": "Web3Wrapper", "FAQ": "FAQ", "SMART_CONTRACTS": "0x 智能合约", "STANDARD_RELAYER_API": "中继方标准API", diff --git a/packages/website/translations/english.json b/packages/website/translations/english.json index 9a1c9b2c8..122d445cb 100644 --- a/packages/website/translations/english.json +++ b/packages/website/translations/english.json @@ -57,11 +57,16 @@ "ABOUT": "about", "CAREERS": "careers", "CONTACT": "contact", + "DEPLOYER": "Deployer", + "JSON_SCHEMAS": "JSON Schemas", + "SOL_COV": "Solidity Coverage", + "SUBPROVIDERS": "Subproviders", "BLOG": "blog", "FORUM": "forum", "CONNECT": "0x Connect", "WHITEPAPER": "whitepaper", "WIKI": "wiki", + "WEB3_WRAPPER": "Web3Wrapper", "FAQ": "FAQ", "SMART_CONTRACTS": "0x smart contracts", "STANDARD_RELAYER_API": "standard relayer API", diff --git a/packages/website/translations/korean.json b/packages/website/translations/korean.json index b9be664e2..dd5f19b16 100644 --- a/packages/website/translations/korean.json +++ b/packages/website/translations/korean.json @@ -56,11 +56,16 @@ "ABOUT": "기업 정보", "CAREERS": "채용", "CONTACT": "문의", + "DEPLOYER": "Deployer", + "JSON_SCHEMAS": "JSON Schemas", + "SOL_COV": "Solidity Coverage", + "SUBPROVIDERS": "Subproviders", "BLOG": "블로그", "FORUM": "포럼", "CONNECT": "0x Connect", "WHITEPAPER": "백서", "WIKI": "위키", + "WEB3_WRAPPER": "Web3Wrapper", "FAQ": "FAQ", "SMART_CONTRACTS": "0x 스마트 계약", "STANDARD_RELAYER_API": "Standard Relayer API", diff --git a/packages/website/translations/russian.json b/packages/website/translations/russian.json index c103960c0..5a8e2c539 100644 --- a/packages/website/translations/russian.json +++ b/packages/website/translations/russian.json @@ -56,11 +56,16 @@ "ABOUT": "Kоманда", "CAREERS": "Карьера", "CONTACT": "Связаться с нами", + "DEPLOYER": "Deployer", + "JSON_SCHEMAS": "JSON Schemas", + "SOL_COV": "Solidity Coverage", + "SUBPROVIDERS": "Subproviders", "BLOG": "Блог", "FORUM": "Форум", "CONNECT": "0x Connect", "WHITEPAPER": "Whitepaper", "WIKI": "Вики", + "WEB3_WRAPPER": "Web3Wrapper", "FAQ": "Документация", "SMART_CONTRACTS": "0x Смарт-контракты ", "STANDARD_RELAYER_API": "standard relayer API", diff --git a/packages/website/translations/spanish.json b/packages/website/translations/spanish.json index c2aaf384c..dd34e805c 100644 --- a/packages/website/translations/spanish.json +++ b/packages/website/translations/spanish.json @@ -57,11 +57,16 @@ "ABOUT": "equipo", "CAREERS": "empleo", "CONTACT": "contacto", + "DEPLOYER": "Deployer", + "JSON_SCHEMAS": "JSON Schemas", + "SOL_COV": "Solidity Coverage", + "SUBPROVIDERS": "Subproviders", "BLOG": "blog", "FORUM": "foro", "CONNECT": "0x Connect", "WHITEPAPER": "documento técnico", "WIKI": "wiki", + "WEB3_WRAPPER": "Web3Wrapper", "FAQ": "preguntas frecuentes", "SMART_CONTRACTS": "0x contratos inteligentes", "STANDARD_RELAYER_API": "API de transmisión estándar", diff --git a/packages/website/ts/components/eth_wrappers.tsx b/packages/website/ts/components/eth_wrappers.tsx index b12c637e5..59afeb50b 100644 --- a/packages/website/ts/components/eth_wrappers.tsx +++ b/packages/website/ts/components/eth_wrappers.tsx @@ -10,7 +10,14 @@ import ReactTooltip = require('react-tooltip'); import { Blockchain } from 'ts/blockchain'; import { EthWethConversionButton } from 'ts/components/eth_weth_conversion_button'; import { Dispatcher } from 'ts/redux/dispatcher'; -import { OutdatedWrappedEtherByNetworkId, Side, Token, TokenByAddress, TokenState } from 'ts/types'; +import { + OutdatedWrappedEtherByNetworkId, + Side, + Token, + TokenByAddress, + TokenState, + TokenStateByAddress, +} from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; @@ -20,13 +27,6 @@ const ICON_DIMENSION = 40; const ETHER_ICON_PATH = '/images/ether.png'; const OUTDATED_WETH_ICON_PATH = '/images/wrapped_eth_gray.png'; -interface OutdatedWETHAddressToIsStateLoaded { - [address: string]: boolean; -} -interface OutdatedWETHStateByAddress { - [address: string]: TokenState; -} - interface EthWrappersProps { networkId: number; blockchain: Blockchain; @@ -39,9 +39,7 @@ interface EthWrappersProps { interface EthWrappersState { ethTokenState: TokenState; - isWethStateLoaded: boolean; - outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded; - outdatedWETHStateByAddress: OutdatedWETHStateByAddress; + outdatedWETHStateByAddress: TokenStateByAddress; } export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersState> { @@ -50,22 +48,20 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt super(props); this._isUnmounted = false; const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); - const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; - const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; + const outdatedWETHStateByAddress: TokenStateByAddress = {}; _.each(outdatedWETHAddresses, outdatedWETHAddress => { - outdatedWETHAddressToIsStateLoaded[outdatedWETHAddress] = false; outdatedWETHStateByAddress[outdatedWETHAddress] = { balance: new BigNumber(0), allowance: new BigNumber(0), + isLoaded: false, }; }); this.state = { - outdatedWETHAddressToIsStateLoaded, outdatedWETHStateByAddress, - isWethStateLoaded: false, ethTokenState: { balance: new BigNumber(0), allowance: new BigNumber(0), + isLoaded: false, }, }; } @@ -169,7 +165,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt {this._renderTokenLink(tokenLabel, etherscanUrl)} </TableRowColumn> <TableRowColumn> - {this.state.isWethStateLoaded ? ( + {this.state.ethTokenState.isLoaded ? ( `${wethBalance.toFixed(configs.AMOUNT_DISPLAY_PRECSION)} WETH` ) : ( <i className="zmdi zmdi-spinner zmdi-hc-spin" /> @@ -183,7 +179,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt networkId={this.props.networkId} isOutdatedWrappedEther={false} direction={Side.Receive} - isDisabled={!this.state.isWethStateLoaded} + isDisabled={!this.state.ethTokenState.isLoaded} ethToken={etherToken} dispatcher={this.props.dispatcher} blockchain={this.props.blockchain} @@ -266,8 +262,8 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt ...etherToken, address: outdatedWETHIfExists.address, }; - const isStateLoaded = this.state.outdatedWETHAddressToIsStateLoaded[outdatedWETHIfExists.address]; const outdatedEtherTokenState = this.state.outdatedWETHStateByAddress[outdatedWETHIfExists.address]; + const isStateLoaded = outdatedEtherTokenState.isLoaded; const balanceInEthIfExists = isStateLoaded ? ZeroEx.toUnitAmount(outdatedEtherTokenState.balance, constants.DECIMAL_PLACES_ETH).toFixed( configs.AMOUNT_DISPLAY_PRECSION, @@ -345,10 +341,15 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt ); } private async _onOutdatedConversionSuccessfulAsync(outdatedWETHAddress: string) { + const currentOutdatedWETHState = this.state.outdatedWETHStateByAddress[outdatedWETHAddress]; this.setState({ - outdatedWETHAddressToIsStateLoaded: { - ...this.state.outdatedWETHAddressToIsStateLoaded, - [outdatedWETHAddress]: false, + outdatedWETHStateByAddress: { + ...this.state.outdatedWETHStateByAddress, + [outdatedWETHAddress]: { + balance: currentOutdatedWETHState.balance, + allowance: currentOutdatedWETHState.allowance, + isLoaded: false, + }, }, }); const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; @@ -357,15 +358,12 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt outdatedWETHAddress, ); this.setState({ - outdatedWETHAddressToIsStateLoaded: { - ...this.state.outdatedWETHAddressToIsStateLoaded, - [outdatedWETHAddress]: true, - }, outdatedWETHStateByAddress: { ...this.state.outdatedWETHStateByAddress, [outdatedWETHAddress]: { balance, allowance, + isLoaded: true, }, }, }); @@ -380,8 +378,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt ); const outdatedWETHAddresses = this._getOutdatedWETHAddresses(); - const outdatedWETHAddressToIsStateLoaded: OutdatedWETHAddressToIsStateLoaded = {}; - const outdatedWETHStateByAddress: OutdatedWETHStateByAddress = {}; + const outdatedWETHStateByAddress: TokenStateByAddress = {}; for (const address of outdatedWETHAddresses) { const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( userAddressIfExists, @@ -390,18 +387,17 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt outdatedWETHStateByAddress[address] = { balance, allowance, + isLoaded: true, }; - outdatedWETHAddressToIsStateLoaded[address] = true; } if (!this._isUnmounted) { this.setState({ outdatedWETHStateByAddress, - outdatedWETHAddressToIsStateLoaded, ethTokenState: { balance: wethBalance, allowance: wethAllowance, + isLoaded: true, }, - isWethStateLoaded: true, }); } } @@ -434,6 +430,7 @@ export class EthWrappers extends React.Component<EthWrappersProps, EthWrappersSt ethTokenState: { balance, allowance, + isLoaded: true, }, }); } diff --git a/packages/website/ts/components/portal.tsx b/packages/website/ts/components/portal.tsx index 5bdb5bde9..59eaca67e 100644 --- a/packages/website/ts/components/portal.tsx +++ b/packages/website/ts/components/portal.tsx @@ -19,12 +19,22 @@ import { TokenBalances } from 'ts/components/token_balances'; import { TopBar } from 'ts/components/top_bar/top_bar'; import { TradeHistory } from 'ts/components/trade_history/trade_history'; import { FlashMessage } from 'ts/components/ui/flash_message'; +import { Wallet } from 'ts/components/wallet'; import { GenerateOrderForm } from 'ts/containers/generate_order_form'; import { localStorage } from 'ts/local_storage/local_storage'; import { Dispatcher } from 'ts/redux/dispatcher'; import { portalOrderSchema } from 'ts/schemas/portal_order_schema'; import { validator } from 'ts/schemas/validator'; -import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress, WebsitePaths } from 'ts/types'; +import { + BlockchainErrs, + Environments, + HashData, + Order, + ProviderType, + ScreenWidths, + TokenByAddress, + WebsitePaths, +} from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; @@ -194,6 +204,12 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> { <div className="py2" style={{ backgroundColor: colors.grey50 }}> {this.props.blockchainIsLoaded ? ( <Switch> + {configs.ENVIRONMENT === Environments.DEVELOPMENT && ( + <Route + path={`${WebsitePaths.Portal}/wallet`} + render={this._renderWallet.bind(this)} + /> + )} <Route path={`${WebsitePaths.Portal}/weth`} render={this._renderEthWrapper.bind(this)} @@ -272,6 +288,28 @@ export class Portal extends React.Component<PortalAllProps, PortalAllState> { 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} + /> + </div> + </div> + ); + } private _renderEthWrapper() { return ( <EthWrappers diff --git a/packages/website/ts/components/portal_menu.tsx b/packages/website/ts/components/portal_menu.tsx index a2f9340c8..9ab611556 100644 --- a/packages/website/ts/components/portal_menu.tsx +++ b/packages/website/ts/components/portal_menu.tsx @@ -1,7 +1,8 @@ import * as _ from 'lodash'; import * as React from 'react'; import { MenuItem } from 'ts/components/ui/menu_item'; -import { WebsitePaths } from 'ts/types'; +import { Environments, WebsitePaths } from 'ts/types'; +import { configs } from 'ts/utils/configs'; export interface PortalMenuProps { menuItemStyle: React.CSSProperties; @@ -57,6 +58,16 @@ export class PortalMenu extends React.Component<PortalMenuProps, PortalMenuState > {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')} </MenuItem> + {configs.ENVIRONMENT === Environments.DEVELOPMENT && ( + <MenuItem + style={this.props.menuItemStyle} + className="py2" + to={`${WebsitePaths.Portal}/wallet`} + onClick={this.props.onClick.bind(this)} + > + {this._renderMenuItemWithIcon('Wallet', 'zmdi-balance-wallet')} + </MenuItem> + )} </div> ); } diff --git a/packages/website/ts/components/sidebar_header.tsx b/packages/website/ts/components/sidebar_header.tsx index 519b23d80..a0ea869fb 100644 --- a/packages/website/ts/components/sidebar_header.tsx +++ b/packages/website/ts/components/sidebar_header.tsx @@ -4,15 +4,9 @@ import * as React from 'react'; const SHOW_DURATION_MS = 4000; -const titleToIcon: { [title: string]: string } = { - '0x.js': 'zeroExJs.png', - '0x Connect': 'connect.png', - '0x Smart Contracts': 'contracts.png', - Wiki: 'wiki.png', -}; - interface SidebarHeaderProps { title: string; + iconUrl: string; } interface SidebarHeaderState {} @@ -32,7 +26,7 @@ export class SidebarHeader extends React.Component<SidebarHeaderProps, SidebarHe </div> <div className="flex"> <div> - <img src={`/images/doc_icons/${titleToIcon[this.props.title]}`} width="22" /> + <img src={this.props.iconUrl} width="22" /> </div> <div className="pl1" style={{ fontWeight: 600, fontSize: 20, lineHeight: 1.2 }}> {this.props.title} diff --git a/packages/website/ts/components/token_balances.tsx b/packages/website/ts/components/token_balances.tsx index 186393c4f..b4a710ef4 100644 --- a/packages/website/ts/components/token_balances.tsx +++ b/packages/website/ts/components/token_balances.tsx @@ -37,6 +37,7 @@ import { ScreenWidths, Token, TokenByAddress, + TokenStateByAddress, TokenVisibility, } from 'ts/types'; import { configs } from 'ts/utils/configs'; @@ -61,14 +62,6 @@ const styles: Styles = { }, }; -interface TokenStateByAddress { - [address: string]: { - balance: BigNumber; - allowance: BigNumber; - isLoaded: boolean; - }; -} - interface TokenBalancesProps { blockchain: Blockchain; blockchainErr: BlockchainErrs; diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 29c68524c..4b2e82119 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -39,6 +39,7 @@ interface TopBarProps { style?: React.CSSProperties; isNightVersion?: boolean; onVersionSelected?: (semver: string) => void; + sidebarHeader?: React.ReactNode; } interface TopBarState { @@ -120,14 +121,36 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { primaryText={this.props.translate.get(Key.StandardRelayerApi, Deco.CapWords)} /> </a>, - <a - key="subMenuItem-github" - target="_blank" - className="text-decoration-none" - href={constants.URL_GITHUB_ORG} - > - <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="GitHub" /> - </a>, + <Link key="subMenuItem-jsonSchema" to={WebsitePaths.JSONSchemas} className="text-decoration-none"> + <MenuItem + style={{ fontSize: styles.menuItem.fontSize }} + primaryText={this.props.translate.get(Key.JsonSchemas, Deco.CapWords)} + /> + </Link>, + <Link key="subMenuItem-subproviders" to={WebsitePaths.Subproviders} className="text-decoration-none"> + <MenuItem + style={{ fontSize: styles.menuItem.fontSize }} + primaryText={this.props.translate.get(Key.Subproviders, Deco.CapWords)} + /> + </Link>, + <Link key="subMenuItem-web3Wrapper" to={WebsitePaths.Web3Wrapper} className="text-decoration-none"> + <MenuItem + style={{ fontSize: styles.menuItem.fontSize }} + primaryText={this.props.translate.get(Key.Web3Wrapper, Deco.CapWords)} + /> + </Link>, + <Link key="subMenuItem-deployer" to={WebsitePaths.Deployer} className="text-decoration-none"> + <MenuItem + style={{ fontSize: styles.menuItem.fontSize }} + primaryText={this.props.translate.get(Key.Deployer, Deco.CapWords)} + /> + </Link>, + <Link key="subMenuItem-sol-cov" to={WebsitePaths.SolCov} className="text-decoration-none"> + <MenuItem + style={{ fontSize: styles.menuItem.fontSize }} + primaryText={this.props.translate.get(Key.SolCov, Deco.CapWords)} + /> + </Link>, <a key="subMenuItem-whitePaper" target="_blank" @@ -139,6 +162,14 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { primaryText={this.props.translate.get(Key.Whitepaper, Deco.CapWords)} /> </a>, + <a + key="subMenuItem-github" + target="_blank" + className="text-decoration-none" + href={constants.URL_GITHUB_ORG} + > + <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="GitHub" /> + </a>, ]; const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; const fullWidthClasses = isFullWidthPage ? 'pr4' : ''; @@ -277,6 +308,46 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </MenuItem> </Link> )} + {!this._isViewingWeb3WrapperDocs() && ( + <Link to={WebsitePaths.Web3Wrapper} className="text-decoration-none"> + <MenuItem className="py2"> + {this.props.translate.get(Key.Web3Wrapper, Deco.Cap)}{' '} + {this.props.translate.get(Key.Docs, Deco.Cap)} + </MenuItem> + </Link> + )} + {!this._isViewingDeployerDocs() && ( + <Link to={WebsitePaths.Deployer} className="text-decoration-none"> + <MenuItem className="py2"> + {this.props.translate.get(Key.Deployer, Deco.Cap)}{' '} + {this.props.translate.get(Key.Docs, Deco.Cap)} + </MenuItem> + </Link> + )} + {!this._isViewingJsonSchemasDocs() && ( + <Link to={WebsitePaths.JSONSchemas} className="text-decoration-none"> + <MenuItem className="py2"> + {this.props.translate.get(Key.JsonSchemas, Deco.Cap)}{' '} + {this.props.translate.get(Key.Docs, Deco.Cap)} + </MenuItem> + </Link> + )} + {!this._isViewingSolCovDocs() && ( + <Link to={WebsitePaths.SolCov} className="text-decoration-none"> + <MenuItem className="py2"> + {this.props.translate.get(Key.SolCov, Deco.Cap)}{' '} + {this.props.translate.get(Key.Docs, Deco.Cap)} + </MenuItem> + </Link> + )} + {!this._isViewingSubprovidersDocs() && ( + <Link to={WebsitePaths.Subproviders} className="text-decoration-none"> + <MenuItem className="py2"> + {this.props.translate.get(Key.Subproviders, Deco.Cap)}{' '} + {this.props.translate.get(Key.Docs, Deco.Cap)} + </MenuItem> + </Link> + )} {!this._isViewingPortal() && ( <Link to={`${WebsitePaths.Portal}`} className="text-decoration-none"> <MenuItem className="py2"> @@ -304,7 +375,14 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { } private _renderDocsMenu(): React.ReactNode { if ( - (!this._isViewing0xjsDocs() && !this._isViewingSmartContractsDocs() && !this._isViewingConnectDocs()) || + (!this._isViewing0xjsDocs() && + !this._isViewingSmartContractsDocs() && + !this._isViewingWeb3WrapperDocs() && + !this._isViewingDeployerDocs() && + !this._isViewingJsonSchemasDocs() && + !this._isViewingSolCovDocs() && + !this._isViewingSubprovidersDocs() && + !this._isViewingConnectDocs()) || _.isUndefined(this.props.menu) ) { return undefined; @@ -316,7 +394,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { <NestedSidebarMenu topLevelMenu={this.props.menu} menuSubsectionsBySection={this.props.menuSubsectionsBySection} - sidebarHeader={<SidebarHeader title={this.props.docsInfo.displayName} />} + sidebarHeader={this.props.sidebarHeader} shouldDisplaySectionHeaders={false} onMenuItemClick={this._onMenuButtonClick.bind(this)} selectedVersion={this.props.docsVersion} @@ -336,7 +414,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { <NestedSidebarMenu topLevelMenu={this.props.menuSubsectionsBySection} menuSubsectionsBySection={this.props.menuSubsectionsBySection} - sidebarHeader={<SidebarHeader title="Wiki" />} + sidebarHeader={this.props.sidebarHeader} shouldDisplaySectionHeaders={false} onMenuItemClick={this._onMenuButtonClick.bind(this)} /> @@ -392,6 +470,21 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { private _isViewingSmartContractsDocs() { return _.includes(this.props.location.pathname, WebsitePaths.SmartContracts); } + private _isViewingWeb3WrapperDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.Web3Wrapper); + } + private _isViewingDeployerDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.Deployer); + } + private _isViewingJsonSchemasDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.JSONSchemas); + } + private _isViewingSolCovDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.SolCov); + } + private _isViewingSubprovidersDocs() { + return _.includes(this.props.location.pathname, WebsitePaths.Subproviders); + } private _isViewingWiki() { return _.includes(this.props.location.pathname, WebsitePaths.Wiki); } @@ -401,7 +494,12 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { this._isViewing0xjsDocs() || this._isViewingFAQ() || this._isViewingSmartContractsDocs() || + this._isViewingWeb3WrapperDocs() || + this._isViewingDeployerDocs() || + this._isViewingJsonSchemasDocs() || + this._isViewingSolCovDocs() || + this._isViewingSubprovidersDocs() || this._isViewingConnectDocs() ); } -} +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/wallet.tsx b/packages/website/ts/components/wallet.tsx new file mode 100644 index 000000000..8c6ef9cad --- /dev/null +++ b/packages/website/ts/components/wallet.tsx @@ -0,0 +1,373 @@ +import { ZeroEx } from '0x.js'; +import { + colors, + constants as sharedConstants, + EtherscanLinkSuffixes, + Styles, + utils as sharedUtils, +} from '@0xproject/react-shared'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import FlatButton from 'material-ui/FlatButton'; +import { List, ListItem } from 'material-ui/List'; +import NavigationArrowDownward from 'material-ui/svg-icons/navigation/arrow-downward'; +import NavigationArrowUpward from 'material-ui/svg-icons/navigation/arrow-upward'; +import * as React from 'react'; +import ReactTooltip = require('react-tooltip'); +import firstBy = require('thenby'); + +import { Blockchain } from 'ts/blockchain'; +import { AllowanceToggle } from 'ts/components/inputs/allowance_toggle'; +import { Identicon } from 'ts/components/ui/identicon'; +import { TokenIcon } from 'ts/components/ui/token_icon'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { BalanceErrs, BlockchainErrs, Token, TokenByAddress, TokenState, TokenStateByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { utils } from 'ts/utils/utils'; + +export interface WalletProps { + userAddress?: string; + networkId?: number; + blockchain: Blockchain; + blockchainIsLoaded: boolean; + blockchainErr: BlockchainErrs; + dispatcher: Dispatcher; + tokenByAddress: TokenByAddress; + trackedTokens: Token[]; + userEtherBalanceInWei: BigNumber; + lastForceTokenStateRefetch: number; +} + +interface WalletState { + trackedTokenStateByAddress: TokenStateByAddress; +} + +enum WrappedEtherAction { + Wrap, + Unwrap, +} + +interface AllowanceToggleConfig { + token: Token; + tokenState: TokenState; +} + +interface AccessoryItemConfig { + wrappedEtherAction?: WrappedEtherAction; + allowanceToggleConfig?: AllowanceToggleConfig; +} + +const styles: Styles = { + wallet: { + width: 346, + backgroundColor: colors.white, + borderBottomRightRadius: 10, + borderBottomLeftRadius: 10, + borderTopRightRadius: 10, + borderTopLeftRadius: 10, + boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + overflow: 'hidden', + }, + list: { + padding: 0, + }, + tokenItemInnerDiv: { + paddingLeft: 60, + }, + headerItemInnerDiv: { + paddingLeft: 65, + }, + footerItemInnerDiv: { + paddingLeft: 24, + }, + borderedItem: { + borderBottomColor: colors.walletBorder, + borderBottomStyle: 'solid', + borderWidth: 1, + }, + tokenItem: { + backgroundColor: colors.walletDefaultItemBackground, + paddingTop: 8, + paddingBottom: 8, + }, + headerItem: { + paddingTop: 8, + paddingBottom: 8, + }, + wrappedEtherButtonLabel: { + fontSize: 12, + }, + amountLabel: { + fontWeight: 'bold', + color: colors.black, + }, +}; + +const ETHER_ICON_PATH = '/images/ether.png'; +const ETHER_TOKEN_SYMBOL = 'WETH'; +const ZRX_TOKEN_SYMBOL = 'ZRX'; +const ETHER_SYMBOL = 'ETH'; +const ICON_DIMENSION = 24; +const TOKEN_AMOUNT_DISPLAY_PRECISION = 3; + +export class Wallet extends React.Component<WalletProps, WalletState> { + private _isUnmounted: boolean; + constructor(props: WalletProps) { + super(props); + this._isUnmounted = false; + const initialTrackedTokenStateByAddress = this._getInitialTrackedTokenStateByAddress(props.trackedTokens); + this.state = { + trackedTokenStateByAddress: initialTrackedTokenStateByAddress, + }; + } + public componentWillMount() { + const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); + // tslint:disable-next-line:no-floating-promises + this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); + } + public componentWillUnmount() { + this._isUnmounted = true; + } + public componentWillReceiveProps(nextProps: WalletProps) { + if ( + nextProps.userAddress !== this.props.userAddress || + nextProps.networkId !== this.props.networkId || + nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch + ) { + const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); + // tslint:disable-next-line:no-floating-promises + this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); + } + if (!_.isEqual(nextProps.trackedTokens, this.props.trackedTokens)) { + const newTokens = _.difference(nextProps.trackedTokens, this.props.trackedTokens); + const newTokenAddresses = _.map(newTokens, token => token.address); + // Add placeholder entry for this token to the state, since fetching the + // balance/allowance is asynchronous + const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + _.each(newTokenAddresses, (tokenAddress: string) => { + trackedTokenStateByAddress[tokenAddress] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + isLoaded: false, + }; + }); + this.setState({ + trackedTokenStateByAddress, + }); + // Fetch the actual balance/allowance. + // tslint:disable-next-line:no-floating-promises + this._fetchBalancesAndAllowancesAsync(newTokenAddresses); + } + } + public render() { + const isReadyToRender = this.props.blockchainIsLoaded && this.props.blockchainErr === BlockchainErrs.NoError; + return <div style={styles.wallet}>{isReadyToRender && this._renderRows()}</div>; + } + private _renderRows() { + return ( + <List style={styles.list}> + {_.concat( + this._renderHeaderRows(), + this._renderEthRows(), + this._renderTokenRows(), + this._renderFooterRows(), + )} + </List> + ); + } + private _renderHeaderRows() { + const userAddress = this.props.userAddress; + const primaryText = utils.getAddressBeginAndEnd(userAddress); + return ( + <ListItem + primaryText={primaryText} + leftIcon={<Identicon address={userAddress} diameter={ICON_DIMENSION} />} + style={{ ...styles.headerItem, ...styles.borderedItem }} + innerDivStyle={styles.headerItemInnerDiv} + /> + ); + } + private _renderFooterRows() { + const primaryText = '+ other tokens'; + return ( + <ListItem primaryText={primaryText} style={styles.borderedItem} innerDivStyle={styles.footerItemInnerDiv} /> + ); + } + private _renderEthRows() { + const primaryText = this._renderAmount( + this.props.userEtherBalanceInWei, + constants.DECIMAL_PLACES_ETH, + ETHER_SYMBOL, + ); + const accessoryItemConfig = { + wrappedEtherAction: WrappedEtherAction.Wrap, + }; + return ( + <ListItem + primaryText={primaryText} + leftIcon={<img style={{ width: ICON_DIMENSION, height: ICON_DIMENSION }} src={ETHER_ICON_PATH} />} + rightAvatar={this._renderAccessoryItems(accessoryItemConfig)} + style={{ ...styles.tokenItem, ...styles.borderedItem }} + innerDivStyle={styles.tokenItemInnerDiv} + /> + ); + } + private _renderTokenRows() { + const trackedTokens = this.props.trackedTokens; + const trackedTokensStartingWithEtherToken = trackedTokens.sort( + firstBy((t: Token) => t.symbol !== ETHER_TOKEN_SYMBOL) + .thenBy((t: Token) => t.symbol !== ZRX_TOKEN_SYMBOL) + .thenBy('address'), + ); + return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); + } + private _renderTokenRow(token: Token) { + const tokenState = this.state.trackedTokenStateByAddress[token.address]; + const tokenLink = sharedUtils.getEtherScanLinkIfExists( + token.address, + this.props.networkId, + EtherscanLinkSuffixes.Address, + ); + const amount = this._renderAmount(tokenState.balance, token.decimals, token.symbol); + const wrappedEtherAction = token.symbol === ETHER_TOKEN_SYMBOL ? WrappedEtherAction.Unwrap : undefined; + const accessoryItemConfig: AccessoryItemConfig = { + wrappedEtherAction, + allowanceToggleConfig: { + token, + tokenState, + }, + }; + return ( + <ListItem + primaryText={amount} + leftIcon={this._renderTokenIcon(token, tokenLink)} + rightAvatar={this._renderAccessoryItems(accessoryItemConfig)} + style={{ ...styles.tokenItem, ...styles.borderedItem }} + innerDivStyle={styles.tokenItemInnerDiv} + /> + ); + } + private _renderAccessoryItems(config: AccessoryItemConfig) { + const shouldShowWrappedEtherAction = !_.isUndefined(config.wrappedEtherAction); + const shouldShowToggle = !_.isUndefined(config.allowanceToggleConfig); + return ( + <div style={{ width: 160 }}> + <div className="flex"> + <div className="flex-auto"> + {shouldShowWrappedEtherAction && this._renderWrappedEtherButton(config.wrappedEtherAction)} + </div> + <div className="flex-last py1"> + {shouldShowToggle && this._renderAllowanceToggle(config.allowanceToggleConfig)} + </div> + </div> + </div> + ); + } + private _renderAllowanceToggle(config: AllowanceToggleConfig) { + return ( + <AllowanceToggle + networkId={this.props.networkId} + blockchain={this.props.blockchain} + dispatcher={this.props.dispatcher} + token={config.token} + tokenState={config.tokenState} + onErrorOccurred={_.noop} // TODO: Error handling + userAddress={this.props.userAddress} + isDisabled={!config.tokenState.isLoaded} + refetchTokenStateAsync={this._refetchTokenStateAsync.bind(this, config.token.address)} + /> + ); + } + private _renderAmount(amount: BigNumber, decimals: number, symbol: string) { + 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 _renderTokenIcon(token: Token, tokenLink?: string) { + const tooltipId = `tooltip-${token.address}`; + const tokenIcon = <TokenIcon token={token} diameter={ICON_DIMENSION} />; + if (_.isUndefined(tokenLink)) { + return tokenIcon; + } else { + return ( + <a href={tokenLink} target="_blank" style={{ textDecoration: 'none' }}> + {tokenIcon} + </a> + ); + } + } + private _renderWrappedEtherButton(action: WrappedEtherAction) { + let buttonLabel; + let buttonIcon; + switch (action) { + case WrappedEtherAction.Wrap: + buttonLabel = 'wrap'; + buttonIcon = <NavigationArrowDownward />; + break; + case WrappedEtherAction.Unwrap: + buttonLabel = 'unwrap'; + buttonIcon = <NavigationArrowUpward />; + break; + default: + throw utils.spawnSwitchErr('wrappedEtherAction', action); + } + return ( + <FlatButton + label={buttonLabel} + labelPosition="after" + primary={true} + icon={buttonIcon} + labelStyle={styles.wrappedEtherButtonLabel} + /> + ); + } + private _getInitialTrackedTokenStateByAddress(trackedTokens: Token[]) { + const trackedTokenStateByAddress: TokenStateByAddress = {}; + _.each(trackedTokens, token => { + trackedTokenStateByAddress[token.address] = { + balance: new BigNumber(0), + allowance: new BigNumber(0), + isLoaded: false, + }; + }); + return trackedTokenStateByAddress; + } + private async _fetchBalancesAndAllowancesAsync(tokenAddresses: string[]) { + const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; + for (const tokenAddress of tokenAddresses) { + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + userAddressIfExists, + tokenAddress, + ); + trackedTokenStateByAddress[tokenAddress] = { + balance, + allowance, + isLoaded: true, + }; + } + if (!this._isUnmounted) { + this.setState({ + trackedTokenStateByAddress, + }); + } + } + private async _refetchTokenStateAsync(tokenAddress: string) { + const userAddressIfExists = _.isEmpty(this.props.userAddress) ? undefined : this.props.userAddress; + const [balance, allowance] = await this.props.blockchain.getTokenBalanceAndAllowanceAsync( + userAddressIfExists, + tokenAddress, + ); + this.setState({ + trackedTokenStateByAddress: { + ...this.state.trackedTokenStateByAddress, + [tokenAddress]: { + balance, + allowance, + isLoaded: true, + }, + }, + }); + } +} diff --git a/packages/website/ts/containers/deployer_documentation.ts b/packages/website/ts/containers/deployer_documentation.ts new file mode 100644 index 000000000..4e1c41f8c --- /dev/null +++ b/packages/website/ts/containers/deployer_documentation.ts @@ -0,0 +1,102 @@ +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/deployer/introduction'); +const InstallationMarkdown = require('md/docs/deployer/installation'); +const UsageMarkdown = require('md/docs/deployer/usage'); +/* tslint:enable:no-var-requires */ + +const docSections = { + introduction: 'introduction', + installation: 'installation', + usage: 'usage', + compiler: 'compiler', + deployer: 'deployer', + types: docConstants.TYPES_SECTION_NAME, +}; + +const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.Deployer, + type: SupportedDocJson.TypeDoc, + displayName: 'Deployer', + 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: { + [docSections.introduction]: IntroMarkdown, + [docSections.installation]: InstallationMarkdown, + [docSections.usage]: UsageMarkdown, + }, + sectionNameToModulePath: { + [docSections.compiler]: ['"deployer/src/compiler"'], + [docSections.deployer]: ['"deployer/src/deployer"'], + [docSections.types]: ['"deployer/src/utils/types"', '"types/src/index"'], + }, + menuSubsectionToVersionWhenIntroduced: {}, + sections: docSections, + visibleConstructors: [docSections.compiler, docSections.deployer], + 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: 'https://github.com/ethereum/wiki/wiki/JavaScript-API', + BigNumber: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127', + ContractInstance: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L98', + }, + typeNameToPrefix: { + ContractInstance: 'Web3', + }, + }, +}; +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/json_schemas_documentation.ts b/packages/website/ts/containers/json_schemas_documentation.ts new file mode 100644 index 000000000..154c65ffc --- /dev/null +++ b/packages/website/ts/containers/json_schemas_documentation.ts @@ -0,0 +1,89 @@ +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/json_schemas/introduction'); +const InstallationMarkdown = require('md/docs/json_schemas/installation'); +const UsageMarkdown = require('md/docs/json_schemas/usage'); +const SchemasMarkdown = require('md/docs/json_schemas/schemas'); +/* tslint:enable:no-var-requires */ + +const docSections = { + introduction: 'introduction', + installation: 'installation', + usage: 'usage', + schemaValidator: 'schemaValidator', + schemas: 'schemas', +}; + +const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.JSONSchemas, + type: SupportedDocJson.TypeDoc, + displayName: 'JSON Schemas', + packageUrl: 'https://github.com/0xProject/0x-monorepo', + menu: { + introduction: [docSections.introduction], + install: [docSections.installation], + usage: [docSections.usage], + schemaValidator: [docSections.schemaValidator], + schemas: [docSections.schemas], + }, + sectionNameToMarkdown: { + [docSections.introduction]: IntroMarkdown, + [docSections.installation]: InstallationMarkdown, + [docSections.schemas]: SchemasMarkdown, + [docSections.usage]: UsageMarkdown, + }, + sectionNameToModulePath: { + [docSections.schemaValidator]: ['"json-schemas/src/schema_validator"'], + }, + menuSubsectionToVersionWhenIntroduced: {}, + sections: docSections, + visibleConstructors: [docSections.schemaValidator], + 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: [], + typeNameToExternalLink: { + Schema: + 'https://github.com/tdegrunt/jsonschema/blob/5c2edd4baba149964aec0f23c87ad12c25a50dfb/lib/index.d.ts#L49', + }, + }, +}; +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/sol_cov_documentation.ts b/packages/website/ts/containers/sol_cov_documentation.ts new file mode 100644 index 000000000..2b901ec6f --- /dev/null +++ b/packages/website/ts/containers/sol_cov_documentation.ts @@ -0,0 +1,89 @@ +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/sol_cov/introduction'); +const InstallationMarkdown = require('md/docs/sol_cov/installation'); +const UsageMarkdown = require('md/docs/sol_cov/usage'); +/* tslint:enable:no-var-requires */ + +const docSections = { + introduction: 'introduction', + installation: 'installation', + usage: 'usage', + coverageSubprovider: 'coverageSubprovider', + types: docConstants.TYPES_SECTION_NAME, +}; + +const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.SolCov, + type: SupportedDocJson.TypeDoc, + displayName: 'Sol-cov', + packageUrl: 'https://github.com/0xProject/0x-monorepo', + menu: { + introduction: [docSections.introduction], + install: [docSections.installation], + usage: [docSections.usage], + coverageSubprovider: [docSections.coverageSubprovider], + types: [docSections.types], + }, + sectionNameToMarkdown: { + [docSections.introduction]: IntroMarkdown, + [docSections.installation]: InstallationMarkdown, + [docSections.usage]: UsageMarkdown, + }, + sectionNameToModulePath: { + [docSections.coverageSubprovider]: ['"sol-cov/src/coverage_subprovider"'], + [docSections.types]: ['"subproviders/src/types"'], + }, + menuSubsectionToVersionWhenIntroduced: {}, + sections: docSections, + visibleConstructors: [docSections.coverageSubprovider], + 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: ['NextCallback', 'OnNextCompleted', 'ErrorCallback'], + typeNameToExternalLink: {}, + typeNameToPrefix: { + JSONRPCRequestPayload: 'Web3', + }, + typeNameToDocSection: {}, + }, +}; +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/subproviders_documentation.ts b/packages/website/ts/containers/subproviders_documentation.ts new file mode 100644 index 000000000..7aa05f9a5 --- /dev/null +++ b/packages/website/ts/containers/subproviders_documentation.ts @@ -0,0 +1,141 @@ +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/subproviders/introduction'); +const InstallationMarkdown = require('md/docs/subproviders/installation'); +const LedgerNodeHidMarkdown = require('md/docs/subproviders/ledger_node_hid'); +/* tslint:enable:no-var-requires */ + +const docSections = { + introduction: 'introduction', + installation: 'installation', + subprovider: 'subprovider', + ledgerSubprovider: 'ledgerSubprovider', + ledgerNodeHid: 'ledger-node-hid-issue', + factoryMethods: 'factory-methods', + emptyWalletSubprovider: 'emptyWalletSubprovider', + fakeGasEstimateSubprovider: 'fakeGasEstimateSubprovider', + injectedWeb3Subprovider: 'injectedWeb3Subprovider', + redundantRPCSubprovider: 'redundantRPCSubprovider', + ganacheSubprovider: 'ganacheSubprovider', + nonceTrackerSubprovider: 'nonceTrackerSubprovider', + types: docConstants.TYPES_SECTION_NAME, +}; + +const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.Subproviders, + type: SupportedDocJson.TypeDoc, + displayName: 'Subproviders', + packageUrl: 'https://github.com/0xProject/0x-monorepo', + menu: { + introduction: [docSections.introduction], + install: [docSections.installation], + subprovider: [docSections.subprovider], + ['ledger-subprovider']: [docSections.ledgerSubprovider], + ['ledger-node-hid-issue']: [docSections.ledgerNodeHid], + ['factory-methods']: [docSections.factoryMethods], + ['emptyWallet-subprovider']: [docSections.emptyWalletSubprovider], + ['fakeGasEstimate-subprovider']: [docSections.fakeGasEstimateSubprovider], + ['injectedWeb3-subprovider']: [docSections.injectedWeb3Subprovider], + ['redundantRPC-subprovider']: [docSections.redundantRPCSubprovider], + ['ganache-subprovider']: [docSections.ganacheSubprovider], + ['nonceTracker-subprovider']: [docSections.nonceTrackerSubprovider], + types: [docSections.types], + }, + sectionNameToMarkdown: { + [docSections.introduction]: IntroMarkdown, + [docSections.installation]: InstallationMarkdown, + [docSections.ledgerNodeHid]: LedgerNodeHidMarkdown, + }, + sectionNameToModulePath: { + [docSections.subprovider]: ['"subproviders/src/subproviders/subprovider"'], + [docSections.ledgerSubprovider]: ['"subproviders/src/subproviders/ledger"'], + [docSections.factoryMethods]: ['"subproviders/src/index"'], + [docSections.emptyWalletSubprovider]: ['"subproviders/src/subproviders/empty_wallet_subprovider"'], + [docSections.fakeGasEstimateSubprovider]: ['"subproviders/src/subproviders/fake_gas_estimate_subprovider"'], + [docSections.injectedWeb3Subprovider]: ['"subproviders/src/subproviders/injected_web3"'], + [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"'], + }, + menuSubsectionToVersionWhenIntroduced: {}, + sections: docSections, + visibleConstructors: [ + docSections.subprovider, + docSections.ledgerSubprovider, + docSections.emptyWalletSubprovider, + docSections.fakeGasEstimateSubprovider, + docSections.injectedWeb3Subprovider, + docSections.redundantRPCSubprovider, + docSections.ganacheSubprovider, + docSections.nonceTrackerSubprovider, + ], + 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: [ + 'Callback', + 'NextCallback', + 'ErrorCallback', + 'ECSignature', + 'JSONRPCRequestPayloadWithMethod', + 'JSONRPCResponsePayload', + 'AccountFetchingConfigs', + 'LedgerEthereumClientFactoryAsync', + 'PartialTxParams', + 'LedgerEthereumClient', + 'LedgerSubproviderConfigs', + ], + typeNameToExternalLink: { + Web3: 'https://github.com/ethereum/wiki/wiki/JavaScript-API', + BigNumber: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127', + JSONRPCRequestPayload: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L137', + JSONRPCResponsePayload: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L144', + Provider: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150', + }, + typeNameToPrefix: { + JSONRPCRequestPayload: 'Web3', + JSONRPCResponsePayload: 'Web3', + Provider: 'Web3', + }, + }, +}; +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/web3_wrapper_documentation.ts b/packages/website/ts/containers/web3_wrapper_documentation.ts new file mode 100644 index 000000000..0a0911b80 --- /dev/null +++ b/packages/website/ts/containers/web3_wrapper_documentation.ts @@ -0,0 +1,103 @@ +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/web3_wrapper/introduction'); +const InstallationMarkdown = require('md/docs/web3_wrapper/installation'); +/* tslint:enable:no-var-requires */ + +const docSections = { + introduction: 'introduction', + installation: 'installation', + web3Wrapper: 'web3Wrapper', + types: docConstants.TYPES_SECTION_NAME, +}; + +const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.Web3Wrapper, + type: SupportedDocJson.TypeDoc, + displayName: 'Web3Wrapper', + packageUrl: 'https://github.com/0xProject/0x-monorepo', + menu: { + introduction: [docSections.introduction], + install: [docSections.installation], + web3Wrapper: [docSections.web3Wrapper], + types: [docSections.types], + }, + sectionNameToMarkdown: { + [docSections.introduction]: IntroMarkdown, + [docSections.installation]: InstallationMarkdown, + }, + sectionNameToModulePath: { + [docSections.web3Wrapper]: ['"web3-wrapper/src/index"'], + [docSections.types]: ['"types/src/index"'], + }, + menuSubsectionToVersionWhenIntroduced: {}, + sections: docSections, + visibleConstructors: [docSections.web3Wrapper], + 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: ['TxData', 'TransactionReceipt', 'RawLogEntry'], + typeNameToExternalLink: { + Web3: 'https://github.com/ethereum/wiki/wiki/JavaScript-API', + Provider: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150', + BigNumber: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127', + LogEntryEvent: 'http://mikemcl.github.io/bignumber.js', + CallData: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L348', + BlockWithoutTransactionData: + 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L314', + LogEntry: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L366', + FilterObject: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L109', + ['Web3.BlockParam']: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L278', + ['Web3.ContractAbi']: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L47', + }, + typeNameToPrefix: { + Provider: 'Web3', + CallData: 'Web3', + BlockWithoutTransactionData: 'Web3', + LogEntry: 'Web3', + FilterObject: 'Web3', + }, + typeNameToDocSection: { + Web3Wrapper: docSections.web3Wrapper, + }, + }, +}; +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/zero_ex_js_documentation.ts b/packages/website/ts/containers/zero_ex_js_documentation.ts index ec30780c3..6e893133f 100644 --- a/packages/website/ts/containers/zero_ex_js_documentation.ts +++ b/packages/website/ts/containers/zero_ex_js_documentation.ts @@ -90,7 +90,14 @@ const docsInfoConfig: DocsInfoConfig = { '"0x.js/src/order_watcher/order_state_watcher"', '"src/order_watcher/order_state_watcher"', ], - [zeroExJsDocSections.types]: ['"0x.js/src/types"', '"src/types"', '"types/src/index"'], + [zeroExJsDocSections.types]: [ + '"0x.js/src/types"', + '"src/types"', + '"types/src/index"', + '"0x.js/src/contract_wrappers/generated/ether_token"', + '"0x.js/src/contract_wrappers/generated/token"', + '"0x.js/src/contract_wrappers/generated/exchange"', + ], }, menuSubsectionToVersionWhenIntroduced: { [zeroExJsDocSections.etherToken]: '0.7.1', @@ -151,7 +158,6 @@ const docsInfoConfig: DocsInfoConfig = { 'TransactionOpts', 'ContractEventArg', 'LogEvent', - 'LogEntry', 'DecodedLogEvent', 'EventWatcherCallback', 'OnOrderStateChangeCallback', @@ -167,6 +173,7 @@ const docsInfoConfig: DocsInfoConfig = { DecodedLogEntryEvent: 'Web3', LogEntryEvent: 'Web3', CallData: 'Web3', + LogEntry: 'Web3', }, typeNameToExternalLink: { Web3: constants.URL_WEB3_DOCS, @@ -174,6 +181,7 @@ const docsInfoConfig: DocsInfoConfig = { BigNumber: constants.URL_BIGNUMBERJS_GITHUB, DecodedLogEntryEvent: constants.URL_WEB3_DECODED_LOG_ENTRY_EVENT, LogEntryEvent: constants.URL_WEB3_LOG_ENTRY_EVENT, + LogEntry: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L366', }, typeNameToDocSection: { ExchangeWrapper: 'exchange', diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index b24517e39..d542bd804 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -45,6 +45,21 @@ const LazySmartContractsDocumentation = createLazyComponent('Documentation', asy const LazyConnectDocumentation = createLazyComponent('Documentation', async () => System.import<any>(/* webpackChunkName: "connectDocs" */ 'ts/containers/connect_documentation'), ); +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 LazyJSONSchemasDocumentation = createLazyComponent('Documentation', async () => + System.import<any>(/* webpackChunkName: "jsonSchemasDocs" */ 'ts/containers/json_schemas_documentation'), +); +const LazySolCovDocumentation = createLazyComponent('Documentation', async () => + System.import<any>(/* webpackChunkName: "solCovDocs" */ 'ts/containers/sol_cov_documentation'), +); +const LazySubprovidersDocumentation = createLazyComponent('Documentation', async () => + System.import<any>(/* webpackChunkName: "subproviderDocs" */ 'ts/containers/subproviders_documentation'), +); analytics.init(); // tslint:disable-next-line:no-floating-promises @@ -65,6 +80,20 @@ 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.SolCov}/:version?`} component={LazySolCovDocumentation} /> + <Route + path={`${WebsitePaths.JSONSchemas}/:version?`} + component={LazyJSONSchemasDocumentation} + /> + <Route + path={`${WebsitePaths.Subproviders}/:version?`} + component={LazySubprovidersDocumentation} + /> + <Route + path={`${WebsitePaths.Web3Wrapper}/:version?`} + component={LazyWeb3WrapperDocumentation} + /> <Route path={`${WebsitePaths.SmartContracts}/:version?`} component={LazySmartContractsDocumentation} diff --git a/packages/website/ts/pages/documentation/doc_page.tsx b/packages/website/ts/pages/documentation/doc_page.tsx index 1281219c6..8ec1a023d 100644 --- a/packages/website/ts/pages/documentation/doc_page.tsx +++ b/packages/website/ts/pages/documentation/doc_page.tsx @@ -15,19 +15,25 @@ import { docUtils } from 'ts/utils/doc_utils'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; +const isDevelopment = configs.ENVIRONMENT === Environments.DEVELOPMENT; +const DEFAULT_ICON = 'docs.png'; const ZERO_EX_JS_VERSION_MISSING_TOPLEVEL_PATH = '0.32.4'; -const isDevelopment = configs.ENVIRONMENT === Environments.DEVELOPMENT; -const docIdToS3BucketName: { [id: string]: string } = { - [DocPackages.ZeroExJs]: isDevelopment ? 'staging-0xjs-docs-jsons' : '0xjs-docs-jsons', - [DocPackages.SmartContracts]: 'smart-contracts-docs-json', - [DocPackages.Connect]: isDevelopment ? 'staging-connect-docs-jsons' : 'connect-docs-jsons', +const idToIcon: { [id: string]: string } = { + [DocPackages.ZeroExJs]: 'zeroExJs.png', + [DocPackages.Connect]: 'connect.png', + [DocPackages.SmartContracts]: 'contracts.png', }; const docIdToSubpackageName: { [id: string]: string } = { [DocPackages.ZeroExJs]: '0x.js', [DocPackages.Connect]: 'connect', [DocPackages.SmartContracts]: 'contracts', + [DocPackages.Web3Wrapper]: 'web3-wrapper', + [DocPackages.Deployer]: 'deployer', + [DocPackages.JSONSchemas]: 'json-schemas', + [DocPackages.SolCov]: 'sol-cov', + [DocPackages.Subproviders]: 'subproviders', }; export interface DocPageProps { @@ -63,12 +69,13 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> { public componentWillUnmount() { this._isUnmounted = true; } - public render() { const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) ? {} : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat); const sourceUrl = this._getSourceUrl(); + const iconFileName = idToIcon[this.props.docsInfo.id] || DEFAULT_ICON; + const iconUrl = `/images/doc_icons/${iconFileName}`; return ( <div> <DocumentTitle title={`${this.props.docsInfo.displayName} Documentation`} /> @@ -82,13 +89,14 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> { docsInfo={this.props.docsInfo} translate={this.props.translate} onVersionSelected={this._onVersionSelected.bind(this)} + sidebarHeader={<SidebarHeader title={this.props.docsInfo.displayName} iconUrl={iconUrl} />} /> <Documentation selectedVersion={this.props.docsVersion} availableVersions={this.props.availableDocVersions} docsInfo={this.props.docsInfo} docAgnosticFormat={this.state.docAgnosticFormat} - sidebarHeader={<SidebarHeader title={this.props.docsInfo.displayName} />} + sidebarHeader={<SidebarHeader title={this.props.docsInfo.displayName} iconUrl={iconUrl} />} sourceUrl={sourceUrl} topBarHeight={60} onVersionSelected={this._onVersionSelected.bind(this)} @@ -97,25 +105,25 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> { ); } private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> { - const s3BucketName = docIdToS3BucketName[this.props.docsInfo.id]; - const docsJsonRoot = `${constants.S3_BUCKET_ROOT}/${s3BucketName}`; - const versionToFileName = await docUtils.getVersionToFileNameAsync(docsJsonRoot); - const versions = _.keys(versionToFileName); + const folderName = docIdToSubpackageName[this.props.docsInfo.id]; + const docBucketRoot = isDevelopment ? constants.S3_STAGING_DOC_BUCKET_ROOT : constants.S3_DOC_BUCKET_ROOT; + const versionToFilePath = await docUtils.getVersionToFilePathAsync(docBucketRoot, folderName); + const versions = _.keys(versionToFilePath); this.props.dispatcher.updateAvailableDocVersions(versions); const sortedVersions = semverSort.desc(versions); const latestVersion = sortedVersions[0]; let versionToFetch = latestVersion; if (!_.isUndefined(preferredVersionIfExists)) { - const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists]; + const preferredVersionFileNameIfExists = versionToFilePath[preferredVersionIfExists]; if (!_.isUndefined(preferredVersionFileNameIfExists)) { versionToFetch = preferredVersionIfExists; } } this.props.dispatcher.updateCurrentDocsVersion(versionToFetch); - const versionFileNameToFetch = versionToFileName[versionToFetch]; - const versionDocObj = await docUtils.getJSONDocFileAsync(versionFileNameToFetch, docsJsonRoot); + const versionFilePathToFetch = versionToFilePath[versionToFetch]; + const versionDocObj = await docUtils.getJSONDocFileAsync(versionFilePathToFetch, docBucketRoot); const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj); if (!this._isUnmounted) { diff --git a/packages/website/ts/pages/faq/faq.tsx b/packages/website/ts/pages/faq/faq.tsx index 1be3c3565..701031d44 100644 --- a/packages/website/ts/pages/faq/faq.tsx +++ b/packages/website/ts/pages/faq/faq.tsx @@ -238,8 +238,8 @@ const sections: FAQSection[] = [ answer: <div>1,000,000,000 ZRX. Fixed supply.</div>, }, { - prompt: 'When is the Token Launch? will there be a pre-sale?', - answer: <div>The token launch will be on August 15th, 2017. There will not be a pre-sale.</div>, + prompt: 'When was the token launch? Was there a pre-sale?', + answer: <div>The token launch was on August 15th, 2017. There was no pre-sale.</div>, }, { prompt: 'What will the token launch proceeds be used for?', diff --git a/packages/website/ts/pages/landing/landing.tsx b/packages/website/ts/pages/landing/landing.tsx index 6699e4e6f..c943e3d79 100644 --- a/packages/website/ts/pages/landing/landing.tsx +++ b/packages/website/ts/pages/landing/landing.tsx @@ -40,8 +40,8 @@ const THROTTLE_TIMEOUT = 100; const relayersAndDappProjects: Project[] = [ { - logoFileName: 'ethfinex.png', - projectUrl: constants.PROJECT_URL_ETHFINEX, + logoFileName: 'ercdex.png', + projectUrl: constants.PROJECT_URL_ERC_DEX, }, { logoFileName: 'radar_relay.png', @@ -60,6 +60,10 @@ const relayersAndDappProjects: Project[] = [ projectUrl: constants.PROJECT_URL_DYDX, }, { + logoFileName: 'ethfinex.png', + projectUrl: constants.PROJECT_URL_ETHFINEX, + }, + { logoFileName: 'melonport.png', projectUrl: constants.PROJECT_URL_MELONPORT, }, @@ -99,10 +103,6 @@ const relayersAndDappProjects: Project[] = [ logoFileName: 'anx.png', projectUrl: constants.PROJECT_URL_OPEN_ANX, }, - { - logoFileName: 'auctus.png', - projectUrl: constants.PROJECT_URL_AUCTUS, - }, ]; const relayerProjects: Project[] = [ diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index e2dd3a68e..23d1b52fb 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -88,6 +88,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> { ...styles.mainContainers, overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden', }; + const sidebarHeader = <SidebarHeader title="Wiki" iconUrl="/images/doc_icons/wiki.png" />; return ( <div> <DocumentTitle title="0x Protocol Wiki" /> @@ -96,6 +97,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> { location={this.props.location} menuSubsectionsBySection={menuSubsectionsBySection} translate={this.props.translate} + sidebarHeader={sidebarHeader} /> {_.isUndefined(this.state.articlesBySection) ? ( <div className="col col-12" style={mainContainersStyle}> @@ -134,7 +136,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> { <NestedSidebarMenu topLevelMenu={menuSubsectionsBySection} menuSubsectionsBySection={menuSubsectionsBySection} - sidebarHeader={<SidebarHeader title="Wiki" />} + sidebarHeader={sidebarHeader} /> </div> </div> diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index b3e4973fa..104d2e50f 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -21,11 +21,6 @@ export interface TokenByAddress { [address: string]: Token; } -export interface TokenState { - allowance: BigNumber; - balance: BigNumber; -} - export interface AssetToken { address?: string; amount?: BigNumber; @@ -336,7 +331,7 @@ export enum TokenVisibility { TRACKED = 'TRACKED', } -export interface VersionToFileName { +export interface VersionToFilePath { [version: string]: string; } @@ -355,45 +350,22 @@ export enum WebsitePaths { Whitepaper = '/pdfs/0x_white_paper.pdf', SmartContracts = '/docs/contracts', Connect = '/docs/connect', + Web3Wrapper = '/docs/web3_wrapper', + Deployer = '/docs/deployer', + JSONSchemas = '/docs/json-schemas', + SolCov = '/docs/sol-cov', + Subproviders = '/docs/subproviders', } export enum DocPackages { Connect = 'CONNECT', ZeroExJs = 'ZERO_EX_JS', SmartContracts = 'SMART_CONTRACTS', -} - -export interface TimestampMsRange { - startTimestampMs: number; - endTimestampMs: number; -} - -export interface OutdatedWrappedEtherByNetworkId { - [networkId: number]: { - address: string; - timestampMsRange: TimestampMsRange; - }; -} - -export enum SmartContractDocSections { - Introduction = 'Introduction', - Exchange = 'Exchange', - TokenTransferProxy = 'TokenTransferProxy', - TokenRegistry = 'TokenRegistry', - ZRXToken = 'ZRXToken', -} - -export interface MaterialUIPosition { - vertical: 'bottom' | 'top' | 'center'; - horizontal: 'left' | 'middle' | 'right'; -} - -export enum Language { - English = 'EN', - Spanish = 'ES', - Chinese = 'ZH', - Korean = 'KO', - Russian = 'RU', + Web3Wrapper = 'WEB3_WRAPPER', + Deployer = 'DEPLOYER', + JSONSchemas = 'JSON_SCHEMAS', + SolCov = 'SOL_COV', + Subproviders = 'SUBPROVIDERS', } export enum Key { @@ -442,11 +414,16 @@ export enum Key { About = 'ABOUT', Careers = 'CAREERS', Contact = 'CONTACT', + Deployer = 'DEPLOYER', + JsonSchemas = 'JSON_SCHEMAS', + SolCov = 'SOL_COV', + Subproviders = 'SUBPROVIDERS', Blog = 'BLOG', Forum = 'FORUM', Connect = 'CONNECT', Whitepaper = 'WHITEPAPER', Wiki = 'WIKI', + Web3Wrapper = 'WEB3_WRAPPER', And = 'AND', Faq = 'FAQ', SmartContracts = 'SMART_CONTRACTS', @@ -458,15 +435,58 @@ export enum Key { RocketChat = 'ROCKETCHAT', } +export enum SmartContractDocSections { + Introduction = 'Introduction', + Exchange = 'Exchange', + TokenTransferProxy = 'TokenTransferProxy', + TokenRegistry = 'TokenRegistry', + ZRXToken = 'ZRXToken', +} + +export enum Language { + English = 'EN', + Spanish = 'ES', + Chinese = 'ZH', + Korean = 'KO', + Russian = 'RU', +} + export enum Deco { Cap, CapWords, Upper, } +export interface MaterialUIPosition { + vertical: 'bottom' | 'top' | 'center'; + horizontal: 'left' | 'middle' | 'right'; +} + export enum Providers { Parity = 'PARITY', Metamask = 'METAMASK', Mist = 'MIST', } + +export interface TimestampMsRange { + startTimestampMs: number; + endTimestampMs: number; +} + +export interface OutdatedWrappedEtherByNetworkId { + [networkId: number]: { + address: string; + timestampMsRange: TimestampMsRange; + }; +} + +export interface TokenStateByAddress { + [address: string]: TokenState; +} + +export interface TokenState { + balance: BigNumber; + allowance: BigNumber; + isLoaded: boolean; +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index f63e8617e..0584938eb 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -29,12 +29,13 @@ export const constants = { PROVIDER_NAME_GENERIC: 'Injected Web3', PROVIDER_NAME_PUBLIC: '0x Public', ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65', - S3_BUCKET_ROOT: 'https://s3.amazonaws.com', + S3_DOC_BUCKET_ROOT: 'https://s3.amazonaws.com/doc-jsons', + S3_STAGING_DOC_BUCKET_ROOT: 'https://s3.amazonaws.com/staging-doc-jsons', SUCCESS_STATUS: 200, UNAVAILABLE_STATUS: 503, TAKER_FEE: new BigNumber(0), TESTNET_NAME: 'Kovan', - PROJECT_URL_ETHFINEX: 'https://www.bitfinex.com/ethfinex', + PROJECT_URL_ETHFINEX: 'https://www.ethfinex.com/', PROJECT_URL_AMADEUS: 'http://amadeusrelay.org', PROJECT_URL_DDEX: 'https://ddex.io', PROJECT_URL_DECENT_EX: 'https://decent.exchange', diff --git a/packages/website/ts/utils/doc_utils.ts b/packages/website/ts/utils/doc_utils.ts index 6b3f5f378..2a599bcbe 100644 --- a/packages/website/ts/utils/doc_utils.ts +++ b/packages/website/ts/utils/doc_utils.ts @@ -2,21 +2,21 @@ import { DoxityDocObj, TypeDocNode } from '@0xproject/react-docs'; import { logUtils } from '@0xproject/utils'; import findVersions = require('find-versions'); import * as _ from 'lodash'; -import { S3FileObject, VersionToFileName } from 'ts/types'; +import { S3FileObject, VersionToFilePath } from 'ts/types'; import { utils } from 'ts/utils/utils'; import convert = require('xml-js'); export const docUtils = { - async getVersionToFileNameAsync(s3DocJsonRoot: string): Promise<VersionToFileName> { - const versionFileNames = await this.getVersionFileNamesAsync(s3DocJsonRoot); - const versionToFileName: VersionToFileName = {}; - _.each(versionFileNames, fileName => { - const [version] = findVersions(fileName); - versionToFileName[version] = fileName; + async getVersionToFilePathAsync(s3DocJsonRoot: string, folderName: string): Promise<VersionToFilePath> { + const versionFilePaths = await this.getVersionFileNamesAsync(s3DocJsonRoot, folderName); + const versionToFilePath: VersionToFilePath = {}; + _.each(versionFilePaths, filePath => { + const [version] = findVersions(filePath); + versionToFilePath[version] = filePath; }); - return versionToFileName; + return versionToFilePath; }, - async getVersionFileNamesAsync(s3DocJsonRoot: string): Promise<string[]> { + async getVersionFileNamesAsync(s3DocJsonRoot: string, folderName: string): Promise<string[]> { const response = await fetch(s3DocJsonRoot); if (response.status !== 200) { // TODO: Show the user an error message when the docs fail to load @@ -33,13 +33,47 @@ export const docUtils = { ? (responseObj.ListBucketResult.Contents as S3FileObject[]) : [responseObj.ListBucketResult.Contents]; - const versionFileNames = _.map(fileObjs, fileObj => { + /* + * S3 simply pre-fixes files in "folders" with the folder name. Thus, since we + * store docJSONs for multiple packages in a single S3 bucket, we must filter out + * the versionFileNames for a given folder here (ignoring folder entries) + * + * Example S3 response: + * <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> + * <Name>staging-doc-jsons</Name> + * <Prefix/> + * <Marker/> + * <MaxKeys>1000</MaxKeys> + * <IsTruncated>false</IsTruncated> + * <Contents> + * <Key>0xjs/</Key> + * <LastModified>2018-03-16T13:17:46.000Z</LastModified> + * <ETag>"d41d8cd98f00b204e9800998ecf8427e"</ETag> + * <Size>0</Size> + * <StorageClass>STANDARD</StorageClass> + * </Contents> + * <Contents> + * <Key>0xjs/v0.1.0.json</Key> + * <LastModified>2018-03-16T13:18:23.000Z</LastModified> + * <ETag>"b4f7f74913aab4a5ad1e6a58fcb3b274"</ETag> + * <Size>1039050</Size> + * <StorageClass>STANDARD</StorageClass> + * </Contents> + */ + const relevantObjs = _.filter(fileObjs, fileObj => { + const key = fileObj.Key._text; + const isInFolderOfInterest = _.includes(key, folderName); + const isFileEntry = !_.endsWith(key, '/'); + return isInFolderOfInterest && isFileEntry; + }); + + const versionFilePaths = _.map(relevantObjs, fileObj => { return fileObj.Key._text; }); - return versionFileNames; + return versionFilePaths; }, - async getJSONDocFileAsync(fileName: string, s3DocJsonRoot: string): Promise<TypeDocNode | DoxityDocObj> { - const endpoint = `${s3DocJsonRoot}/${fileName}`; + async getJSONDocFileAsync(filePath: string, s3DocJsonRoot: string): Promise<TypeDocNode | DoxityDocObj> { + const endpoint = `${s3DocJsonRoot}/${filePath}`; const response = await fetch(endpoint); if (response.status !== 200) { // TODO: Show the user an error message when the docs fail to load diff --git a/packages/website/webpack.config.js b/packages/website/webpack.config.js index c436888bd..e28e9e064 100644 --- a/packages/website/webpack.config.js +++ b/packages/website/webpack.config.js @@ -11,10 +11,7 @@ module.exports = { }, devtool: 'source-map', resolve: { - modules: [ - path.join(__dirname, '/ts'), - 'node_modules', - ], + modules: [path.join(__dirname, '/ts'), 'node_modules'], extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.md'], alias: { ts: path.join(__dirname, '/ts'), @@ -62,25 +59,28 @@ module.exports = { from: /^\/docs\/.*$/, to: function() { return 'index.html'; - } - } - ] + }, + }, + ], }, disableHostCheck: true, }, - plugins: process.env.NODE_ENV === 'production' ? [ - // Since we do not use moment's locale feature, we exclude them from the bundle. - // This reduces the bundle size by 0.4MB. - new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify(process.env.NODE_ENV) - } - }), - new webpack.optimize.UglifyJsPlugin({ - mangle: { - except: ['BigNumber'] - } - }) - ] : [], + plugins: + process.env.NODE_ENV === 'production' + ? [ + // Since we do not use moment's locale feature, we exclude them from the bundle. + // This reduces the bundle size by 0.4MB. + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV), + }, + }), + new webpack.optimize.UglifyJsPlugin({ + mangle: { + except: ['BigNumber'], + }, + }), + ] + : [], }; |