import * as _ from 'lodash'; import * as React from 'react'; import DocumentTitle = require('react-document-title'); import findVersions = require('find-versions'); import semverSort = require('semver-sort'); import {colors} from 'material-ui/styles'; import CircularProgress from 'material-ui/CircularProgress'; import { scroller, } from 'react-scroll'; import {Dispatcher} from 'ts/redux/dispatcher'; import { SmartContractsDocSections, Styles, DoxityDocObj, TypeDefinitionByName, DocAgnosticFormat, SolidityMethod, Property, CustomType, MenuSubsectionsBySection, Event, Docs, AddressByContractName, Networks, EtherscanLinkSuffixes, } from 'ts/types'; import {TopBar} from 'ts/components/top_bar'; import {utils} from 'ts/utils/utils'; import {docUtils} from 'ts/utils/doc_utils'; import {constants} from 'ts/utils/constants'; import {MethodBlock} from 'ts/pages/documentation/method_block'; import {SourceLink} from 'ts/pages/documentation/source_link'; import {Type} from 'ts/pages/documentation/type'; import {TypeDefinition} from 'ts/pages/documentation/type_definition'; import {MarkdownSection} from 'ts/pages/shared/markdown_section'; import {Comment} from 'ts/pages/documentation/comment'; import {Badge} from 'ts/components/ui/badge'; import {EventDefinition} from 'ts/pages/documentation/event_definition'; import {AnchorTitle} from 'ts/pages/shared/anchor_title'; import {SectionHeader} from 'ts/pages/shared/section_header'; import {NestedSidebarMenu} from 'ts/pages/shared/nested_sidebar_menu'; import {doxityUtils} from 'ts/utils/doxity_utils'; /* tslint:disable:no-var-requires */ const IntroMarkdown = require('md/docs/smart_contracts/introduction'); /* tslint:enable:no-var-requires */ const SCROLL_TO_TIMEOUT = 500; const CUSTOM_PURPLE = '#690596'; const CUSTOM_RED = '#e91751'; const CUSTOM_TURQUOIS = '#058789'; const DOC_JSON_ROOT = constants.S3_SMART_CONTRACTS_DOCUMENTATION_JSON_ROOT; const sectionNameToMarkdown = { [SmartContractsDocSections.Introduction]: IntroMarkdown, }; const networkNameToColor: {[network: string]: string} = { [Networks.kovan]: CUSTOM_PURPLE, [Networks.ropsten]: CUSTOM_RED, [Networks.mainnet]: CUSTOM_TURQUOIS, }; export interface SmartContractsDocumentationAllProps { source: string; location: Location; dispatcher: Dispatcher; docsVersion: string; availableDocVersions: string[]; } interface SmartContractsDocumentationState { docAgnosticFormat?: DocAgnosticFormat; } const styles: Styles = { mainContainers: { position: 'absolute', top: 60, left: 0, bottom: 0, right: 0, overflowZ: 'hidden', overflowY: 'scroll', minHeight: 'calc(100vh - 60px)', WebkitOverflowScrolling: 'touch', }, menuContainer: { borderColor: colors.grey300, maxWidth: 330, marginLeft: 20, }, }; export class SmartContractsDocumentation extends React.Component { constructor(props: SmartContractsDocumentationAllProps) { super(props); this.state = { docAgnosticFormat: undefined, }; } public componentWillMount() { const pathName = this.props.location.pathname; const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1); const versions = findVersions(lastSegment); const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined; this.fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists); } public render() { const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) ? {} : this.getMenuSubsectionsBySection(this.state.docAgnosticFormat); return (
{_.isUndefined(this.state.docAgnosticFormat) ?
Loading documentation...
:

0x Smart Contracts

{this.renderDocumentation()}
}
); } private renderDocumentation(): React.ReactNode { const subMenus = _.values(constants.menuSmartContracts); const orderedSectionNames = _.flatten(subMenus); // Since smart contract method params are all base types, no need to pass // down the typeDefinitionByName const typeDefinitionByName = {}; const sections = _.map(orderedSectionNames, this.renderSection.bind(this, typeDefinitionByName)); return sections; } private renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode { const docSection = this.state.docAgnosticFormat[sectionName]; const markdownFileIfExists = sectionNameToMarkdown[sectionName]; if (!_.isUndefined(markdownFileIfExists)) { return ( ); } if (_.isUndefined(docSection)) { return null; } const sortedProperties = _.sortBy(docSection.properties, 'name'); const propertyDefs = _.map(sortedProperties, this.renderProperty.bind(this)); const sortedMethods = _.sortBy(docSection.methods, 'name'); const methodDefs = _.map(sortedMethods, method => { const isConstructor = false; return this.renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName); }); const sortedEvents = _.sortBy(docSection.events, 'name'); const eventDefs = _.map(sortedEvents, (event: Event, i: number) => { return ( ); }); return (
{this.renderNetworkBadges(sectionName)}
{docSection.comment && } {docSection.constructors.length > 0 &&

Constructor

{this.renderConstructors(docSection.constructors, typeDefinitionByName)}
} {docSection.properties.length > 0 &&

Properties

{propertyDefs}
} {docSection.methods.length > 0 &&

Methods

{methodDefs}
} {docSection.events.length > 0 &&

Events

{eventDefs}
}
); } private renderNetworkBadges(sectionName: string) { const networkToAddressByContractName = constants.contractAddresses[this.props.docsVersion]; const badges = _.map(networkToAddressByContractName, (addressByContractName: AddressByContractName, networkName: string) => { const contractAddress = addressByContractName[sectionName]; const linkIfExists = utils.getEtherScanLinkIfExists( contractAddress, constants.networkIdByName[networkName], EtherscanLinkSuffixes.address, ); return ( ); }); return badges; } private renderConstructors(constructors: SolidityMethod[], typeDefinitionByName: TypeDefinitionByName): React.ReactNode { const constructorDefs = _.map(constructors, constructor => { return this.renderMethodBlocks( constructor, SmartContractsDocSections.zeroEx, constructor.isConstructor, typeDefinitionByName, ); }); return (
{constructorDefs}
); } private renderProperty(property: Property): React.ReactNode { return (
{property.name}: {property.source && } {property.comment && }
); } private renderMethodBlocks(method: SolidityMethod, sectionName: string, isConstructor: boolean, typeDefinitionByName: TypeDefinitionByName): React.ReactNode { return ( ); } private scrollToHash(): void { const hashWithPrefix = this.props.location.hash; let hash = hashWithPrefix.slice(1); if (_.isEmpty(hash)) { hash = 'smartContractsDocs'; // scroll to the top } scroller.scrollTo(hash, {duration: 0, offset: 0, containerId: 'documentation'}); } private getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection { const menuSubsectionsBySection = {} as MenuSubsectionsBySection; if (_.isUndefined(docAgnosticFormat)) { return menuSubsectionsBySection; } const docSections = _.keys(SmartContractsDocSections); _.each(docSections, sectionName => { const docSection = docAgnosticFormat[sectionName]; if (_.isUndefined(docSection)) { return; // no-op } if (sectionName === SmartContractsDocSections.types) { const sortedTypesNames = _.sortBy(docSection.types, 'name'); const typeNames = _.map(sortedTypesNames, t => t.name); menuSubsectionsBySection[sectionName] = typeNames; } else { const sortedEventNames = _.sortBy(docSection.events, 'name'); const eventNames = _.map(sortedEventNames, m => m.name); const sortedMethodNames = _.sortBy(docSection.methods, 'name'); const methodNames = _.map(sortedMethodNames, m => m.name); menuSubsectionsBySection[sectionName] = [...methodNames, ...eventNames]; } }); return menuSubsectionsBySection; } private async fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise { const versionToFileName = await docUtils.getVersionToFileNameAsync(DOC_JSON_ROOT); const versions = _.keys(versionToFileName); this.props.dispatcher.updateAvailableDocVersions(versions); const sortedVersions = semverSort.desc(versions); const latestVersion = sortedVersions[0]; let versionToFetch = latestVersion; if (!_.isUndefined(preferredVersionIfExists)) { const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists]; if (!_.isUndefined(preferredVersionFileNameIfExists)) { versionToFetch = preferredVersionIfExists; } } this.props.dispatcher.updateCurrentDocsVersion(versionToFetch); const versionFileNameToFetch = versionToFileName[versionToFetch]; const versionDocObj = await docUtils.getJSONDocFileAsync(versionFileNameToFetch, DOC_JSON_ROOT); const docAgnosticFormat = doxityUtils.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj); this.setState({ docAgnosticFormat, }, () => { this.scrollToHash(); }); } }