diff options
Diffstat (limited to 'packages/website/ts/pages/wiki/wiki.tsx')
-rw-r--r-- | packages/website/ts/pages/wiki/wiki.tsx | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx new file mode 100644 index 000000000..0e6fc98ab --- /dev/null +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -0,0 +1,210 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import DocumentTitle = require('react-document-title'); +import {colors} from 'material-ui/styles'; +import CircularProgress from 'material-ui/CircularProgress'; +import { + scroller, +} from 'react-scroll'; +import {Styles, Article, ArticlesBySection} from 'ts/types'; +import {TopBar} from 'ts/components/top_bar'; +import {HeaderSizes, WebsitePaths} from 'ts/types'; +import {utils} from 'ts/utils/utils'; +import {constants} from 'ts/utils/constants'; +import {configs} from 'ts/utils/configs'; +import {NestedSidebarMenu} from 'ts/pages/shared/nested_sidebar_menu'; +import {SectionHeader} from 'ts/pages/shared/section_header'; +import {MarkdownSection} from 'ts/pages/shared/markdown_section'; + +const WIKI_NOT_READY_BACKOUT_TIMEOUT_MS = 5000; + +export interface WikiProps { + source: string; + location: Location; +} + +interface WikiState { + articlesBySection: ArticlesBySection; +} + +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 Wiki extends React.Component<WikiProps, WikiState> { + private wikiBackoffTimeoutId: number; + constructor(props: WikiProps) { + super(props); + this.state = { + articlesBySection: undefined, + }; + } + public componentWillMount() { + this.fetchArticlesBySectionAsync(); + } + public componentWillUnmount() { + clearTimeout(this.wikiBackoffTimeoutId); + } + public render() { + const menuSubsectionsBySection = _.isUndefined(this.state.articlesBySection) + ? {} + : this.getMenuSubsectionsBySection(this.state.articlesBySection); + return ( + <div> + <DocumentTitle title="0x Protocol Wiki"/> + <TopBar + blockchainIsLoaded={false} + location={this.props.location} + menuSubsectionsBySection={menuSubsectionsBySection} + shouldFullWidth={true} + /> + {_.isUndefined(this.state.articlesBySection) ? + <div + className="col col-12" + style={styles.mainContainers} + > + <div + className="relative sm-px2 sm-pt2 sm-m1" + style={{height: 122, top: '50%', transform: 'translateY(-50%)'}} + > + <div className="center pb2"> + <CircularProgress size={40} thickness={5} /> + </div> + <div className="center pt2" style={{paddingBottom: 11}}>Loading wiki...</div> + </div> + </div> : + <div + className="mx-auto flex" + style={{color: colors.grey800, height: 43}} + > + <div className="relative col md-col-3 lg-col-3 lg-pl0 md-pl1 sm-hide xs-hide"> + <div + className="border-right absolute pt2" + style={{...styles.menuContainer, ...styles.mainContainers}} + > + <NestedSidebarMenu + topLevelMenu={menuSubsectionsBySection} + menuSubsectionsBySection={menuSubsectionsBySection} + isSectionHeaderClickable={true} + /> + </div> + </div> + <div className="relative col lg-col-9 md-col-9 sm-col-12 col-12"> + <div + id="documentation" + style={styles.mainContainers} + className="absolute" + > + <div id="0xProtocolWiki" /> + <h1 className="md-pl2 sm-pl3"> + <a href={constants.GITHUB_WIKI_URL} target="_blank"> + 0x Protocol Wiki + </a> + </h1> + <div id="wiki"> + {this.renderWikiArticles()} + </div> + </div> + </div> + </div> + } + </div> + ); + } + 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.GITHUB_WIKI_URL}/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 className="mb4 mt3 p3 center" style={{backgroundColor: '#f9f5ef'}}> + See a way to make this article better?{' '} + <a + href={githubLink} + target="_blank" + > + Edit here → + </a> + </div> + </div> + ); + }); + return ( + <div + key={`section-${sectionName}`} + className="py2 pr3 md-pl2 sm-pl3" + > + <SectionHeader sectionName={sectionName} headerSize={HeaderSizes.H1} /> + {renderedArticles} + </div> + ); + } + private scrollToHash(): void { + const hashWithPrefix = this.props.location.hash; + let hash = hashWithPrefix.slice(1); + if (_.isEmpty(hash)) { + hash = '0xProtocolWiki'; // scroll to the top + } + + scroller.scrollTo(hash, {duration: 0, offset: 0, containerId: 'documentation'}); + } + private async fetchArticlesBySectionAsync(): Promise<void> { + 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(() => { + 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(); + utils.consoleLog(`Failed to load wiki: ${response.status} ${errMsg}`); + return; + } + const articlesBySection = await response.json(); + this.setState({ + articlesBySection, + }, () => { + this.scrollToHash(); + }); + } + 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; + } +} |