import { colors, constants as sharedConstants, HeaderSizes, MarkdownSection, NestedSidebarMenu, SectionHeader, Styles, utils as sharedUtils, } from '@0xproject/react-shared'; import { logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; import RaisedButton from 'material-ui/RaisedButton'; import * as React from 'react'; import DocumentTitle = require('react-document-title'); import { scroller } from 'react-scroll'; import { SidebarHeader } from 'ts/components/sidebar_header'; import { TopBar } from 'ts/components/top_bar/top_bar'; import { Dispatcher } from 'ts/redux/dispatcher'; import { Article, ArticlesBySection, WebsitePaths } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; const TOP_BAR_HEIGHT = 60; const WIKI_NOT_READY_BACKOUT_TIMEOUT_MS = 5000; export interface WikiProps { source: string; location: Location; dispatcher: Dispatcher; translate: Translate; } interface WikiState { articlesBySection: ArticlesBySection; isHoveringSidebar: boolean; } const styles: Styles = { mainContainers: { position: 'absolute', top: 1, left: 0, bottom: 0, right: 0, overflowZ: 'hidden', height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, WebkitOverflowScrolling: 'touch', }, menuContainer: { borderColor: colors.grey300, maxWidth: 330, backgroundColor: colors.gray40, }, }; export class Wiki extends React.Component { private _wikiBackoffTimeoutId: number; private _isUnmounted: boolean; constructor(props: WikiProps) { super(props); this._isUnmounted = false; this.state = { articlesBySection: undefined, isHoveringSidebar: false, }; } public componentDidMount() { window.addEventListener('hashchange', this._onHashChanged.bind(this), false); } public componentWillMount() { // tslint:disable-next-line:no-floating-promises this._fetchArticlesBySectionAsync(); } public componentWillUnmount() { this._isUnmounted = true; clearTimeout(this._wikiBackoffTimeoutId); window.removeEventListener('hashchange', this._onHashChanged.bind(this), false); } public render() { const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection) ? {} : this._getMenuSubsectionsBySection(this.state.articlesBySection); const mainContainersStyle: React.CSSProperties = { ...styles.mainContainers, overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden', }; const sidebarHeader = ; return (
{_.isUndefined(this.state.articlesBySection) ? (
Loading wiki...
) : (
{this._renderWikiArticles()}
)}
); } private _renderWikiArticles(): React.ReactNode { const sectionNames = _.keys(this.state.articlesBySection); const sections = _.map(sectionNames, sectionName => this._renderSection(sectionName)); return sections; } private _renderSection(sectionName: string) { const articles = this.state.articlesBySection[sectionName]; const renderedArticles = _.map(articles, (article: Article) => { const githubLink = `${constants.URL_GITHUB_WIKI}/edit/master/${sectionName}/${article.fileName}`; return (
See a way to improve this article?
); }); return (
{renderedArticles}
); } private async _fetchArticlesBySectionAsync(): Promise { const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`; const response = await fetch(endpoint); if (response.status === 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; } if (response.status !== 200) { // TODO: Show the user an error message when the wiki fail to load const errMsg = await response.text(); logUtils.log(`Failed to load wiki: ${response.status} ${errMsg}`); return; } const articlesBySection = await response.json(); if (!this._isUnmounted) { this.setState( { articlesBySection, }, async () => { await utils.onPageLoadAsync(); const hash = this.props.location.hash.slice(1); sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID); }, ); } } private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) { const sectionNames = _.keys(articlesBySection); const menuSubsectionsBySection: { [section: string]: string[] } = {}; for (const sectionName of sectionNames) { const articles = articlesBySection[sectionName]; const articleNames = _.map(articles, article => article.title); menuSubsectionsBySection[sectionName] = articleNames; } return menuSubsectionsBySection; } private _onSidebarHover(event: React.FormEvent) { this.setState({ isHoveringSidebar: true, }); } private _onSidebarHoverOff() { this.setState({ isHoveringSidebar: false, }); } private _onHashChanged(event: any) { const hash = window.location.hash.slice(1); sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID); } }