194 lines
7.6 KiB
TypeScript
194 lines
7.6 KiB
TypeScript
import { ObjectMap } from '@0x/types';
|
|
import * as _ from 'lodash';
|
|
import CircularProgress from 'material-ui/CircularProgress';
|
|
import * as React from 'react';
|
|
import { Link } from 'ts/components/documentation/shared/link';
|
|
import { MarkdownSection } from 'ts/components/documentation/shared/markdown_section';
|
|
import { SidebarHeader } from 'ts/components/documentation/sidebar_header';
|
|
import { NestedSidebarMenu } from 'ts/components/nested_sidebar_menu';
|
|
import { Button } from 'ts/components/ui/button';
|
|
import { Container } from 'ts/components/ui/container';
|
|
import { DevelopersPage } from 'ts/pages/documentation/developers_page';
|
|
import { Dispatcher } from 'ts/redux/dispatcher';
|
|
import { ALink, Article, ArticlesBySection, Deco, HeaderSizes, Key, ScreenWidths } from 'ts/types';
|
|
import { backendClient } from 'ts/utils/backend_client';
|
|
import { colors } from 'ts/utils/colors';
|
|
import { constants } from 'ts/utils/constants';
|
|
import { Translate } from 'ts/utils/translate';
|
|
import { utils } from 'ts/utils/utils';
|
|
|
|
const WIKI_NOT_READY_BACKOUT_TIMEOUT_MS = 5000;
|
|
|
|
export interface WikiProps {
|
|
source: string;
|
|
location: Location;
|
|
dispatcher: Dispatcher;
|
|
translate: Translate;
|
|
screenWidth: ScreenWidths;
|
|
}
|
|
|
|
interface WikiState {
|
|
articlesBySection: ArticlesBySection;
|
|
isHoveringSidebar: boolean;
|
|
}
|
|
|
|
export class Wiki extends React.Component<WikiProps, WikiState> {
|
|
private _wikiBackoffTimeoutId: number;
|
|
private _isUnmounted: boolean;
|
|
constructor(props: WikiProps) {
|
|
super(props);
|
|
this._isUnmounted = false;
|
|
this.state = {
|
|
articlesBySection: undefined,
|
|
isHoveringSidebar: false,
|
|
};
|
|
}
|
|
public componentWillMount(): void {
|
|
// tslint:disable-next-line:no-floating-promises
|
|
this._fetchArticlesBySectionAsync();
|
|
}
|
|
public componentWillUnmount(): void {
|
|
this._isUnmounted = true;
|
|
clearTimeout(this._wikiBackoffTimeoutId);
|
|
}
|
|
public render(): React.ReactNode {
|
|
const sectionNameToLinks =
|
|
this.state.articlesBySection === undefined ? {} : this._getSectionNameToLinks(this.state.articlesBySection);
|
|
|
|
const mainContent =
|
|
this.state.articlesBySection === undefined ? (
|
|
<div className="flex justify-center">{this._renderLoading()}</div>
|
|
) : (
|
|
<div id="wiki" style={{ paddingRight: 2 }}>
|
|
{this._renderWikiArticles()}
|
|
</div>
|
|
);
|
|
const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm;
|
|
const sidebar =
|
|
this.state.articlesBySection === undefined ? (
|
|
<div />
|
|
) : (
|
|
<NestedSidebarMenu
|
|
sidebarHeader={isSmallScreen ? this._renderSidebarHeader() : undefined}
|
|
sectionNameToLinks={sectionNameToLinks}
|
|
screenWidth={this.props.screenWidth}
|
|
/>
|
|
);
|
|
return (
|
|
<DevelopersPage
|
|
sidebar={sidebar}
|
|
mainContent={mainContent}
|
|
location={this.props.location}
|
|
screenWidth={this.props.screenWidth}
|
|
translate={this.props.translate}
|
|
dispatcher={this.props.dispatcher}
|
|
/>
|
|
);
|
|
}
|
|
private _renderSidebarHeader(): React.ReactNode {
|
|
const menuItems = _.map(constants.DEVELOPER_TOPBAR_LINKS, menuItemInfo => {
|
|
return (
|
|
<Link
|
|
key={`menu-item-${menuItemInfo.title}`}
|
|
to={menuItemInfo.to}
|
|
shouldOpenInNewTab={menuItemInfo.shouldOpenInNewTab}
|
|
>
|
|
<Button
|
|
borderRadius="4px"
|
|
padding="0.4em 0.375em"
|
|
width="100%"
|
|
fontColor={colors.grey800}
|
|
fontSize="14px"
|
|
textAlign="left"
|
|
>
|
|
{this.props.translate.get(menuItemInfo.title as Key, Deco.Cap)}
|
|
</Button>
|
|
</Link>
|
|
);
|
|
});
|
|
const wikiTitle = this.props.translate.get(Key.Wiki, Deco.Cap);
|
|
return (
|
|
<Container>
|
|
<SidebarHeader screenWidth={this.props.screenWidth} title={wikiTitle} />
|
|
{menuItems}
|
|
</Container>
|
|
);
|
|
}
|
|
private _renderLoading(): React.ReactNode {
|
|
return (
|
|
<Container className="pt4">
|
|
<Container className="center pb2">
|
|
<CircularProgress size={40} thickness={5} />
|
|
</Container>
|
|
<Container className="center pt2" paddingBottom="11px">
|
|
Loading wiki...
|
|
</Container>
|
|
</Container>
|
|
);
|
|
}
|
|
private _renderWikiArticles(): React.ReactNode {
|
|
const sectionNames = _.keys(this.state.articlesBySection);
|
|
const sections = _.map(sectionNames, sectionName => this._renderSection(sectionName));
|
|
return sections;
|
|
}
|
|
private _renderSection(sectionName: string): React.ReactNode {
|
|
const articles = this.state.articlesBySection[sectionName];
|
|
const renderedArticles = _.map(articles, (article: Article) => {
|
|
const githubLink = `${constants.URL_GITHUB_WIKI}/edit/master/${sectionName}/${article.fileName}`;
|
|
return (
|
|
<div key={`markdown-section-${article.title}`}>
|
|
<MarkdownSection
|
|
sectionName={article.title}
|
|
markdownContent={article.content}
|
|
headerSize={HeaderSizes.H2}
|
|
githubLink={githubLink}
|
|
/>
|
|
</div>
|
|
);
|
|
});
|
|
return <div key={`section-${sectionName}`}>{renderedArticles}</div>;
|
|
}
|
|
private async _fetchArticlesBySectionAsync(): Promise<void> {
|
|
try {
|
|
const articlesBySection = await backendClient.getWikiArticlesBySectionAsync();
|
|
if (!this._isUnmounted) {
|
|
this.setState(
|
|
{
|
|
articlesBySection,
|
|
},
|
|
async () => {
|
|
await utils.onPageLoadPromise;
|
|
const hash = this.props.location.hash.slice(1);
|
|
utils.scrollToHash(hash, constants.SCROLL_CONTAINER_ID);
|
|
},
|
|
);
|
|
}
|
|
} catch (err) {
|
|
const errMsg = `${err}`;
|
|
if (_.includes(errMsg, `${constants.HTTP_NO_CONTENT_STATUS_CODE}`)) {
|
|
// We need to backoff and try fetching again later
|
|
this._wikiBackoffTimeoutId = window.setTimeout(() => {
|
|
// tslint:disable-next-line:no-floating-promises
|
|
this._fetchArticlesBySectionAsync();
|
|
}, WIKI_NOT_READY_BACKOUT_TIMEOUT_MS);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
private _getSectionNameToLinks(articlesBySection: ArticlesBySection): ObjectMap<ALink[]> {
|
|
const sectionNames = _.keys(articlesBySection);
|
|
const sectionNameToLinks: ObjectMap<ALink[]> = {};
|
|
for (const sectionName of sectionNames) {
|
|
const articles = articlesBySection[sectionName];
|
|
const articleLinks = _.map(articles, article => {
|
|
return {
|
|
to: utils.getIdFromName(article.title),
|
|
title: article.title,
|
|
};
|
|
});
|
|
sectionNameToLinks[sectionName] = articleLinks;
|
|
}
|
|
return sectionNameToLinks;
|
|
}
|
|
}
|