| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- import React from 'react';
- import * as url from 'url';
- import { pathUtils } from '@growi/core';
- import axios from 'axios';
- import PropTypes from 'prop-types';
- // eslint-disable-next-line no-unused-vars
- import styles from '../../css/index.css';
- import { LsxContext } from '../util/LsxContext';
- import { TagCacheManagerFactory } from '../util/TagCacheManagerFactory';
- import { LsxListView } from './LsxPageList/LsxListView';
- import { PageNode } from './PageNode';
- export class Lsx extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isLoading: false,
- isError: false,
- isCacheExists: false,
- nodeTree: undefined,
- errorMessage: '',
- };
- this.tagCacheManager = TagCacheManagerFactory.getInstance();
- }
- async componentWillMount() {
- const { lsxContext, forceToFetchData } = this.props;
- // get state object cache
- const stateCache = this.retrieveDataFromCache();
- if (stateCache != null) {
- this.setState({
- isCacheExists: true,
- nodeTree: stateCache.nodeTree,
- isError: stateCache.isError,
- errorMessage: stateCache.errorMessage,
- });
- // switch behavior by forceToFetchData
- if (!forceToFetchData) {
- return; // go to render()
- }
- }
- lsxContext.parse();
- this.setState({ isLoading: true });
- // add slash ensure not to forward match to another page
- // ex: '/Java/' not to match to '/JavaScript'
- const pagePath = pathUtils.addTrailingSlash(lsxContext.pagePath);
- try {
- const res = await axios.get('/_api/plugins/lsx', {
- params: {
- pagePath,
- options: lsxContext.options,
- },
- });
- if (res.data.ok) {
- lsxContext.toppageViewersCount = res.data.toppageViewersCount;
- const nodeTree = this.generatePageNodeTree(pagePath, res.data.pages);
- this.setState({ nodeTree });
- }
- }
- catch (error) {
- this.setState({ isError: true, errorMessage: error.message });
- }
- finally {
- this.setState({ isLoading: false });
- // store to sessionStorage
- this.tagCacheManager.cacheState(lsxContext, this.state);
- }
- }
- retrieveDataFromCache() {
- const { lsxContext } = this.props;
- // get state object cache
- const stateCache = this.tagCacheManager.getStateCache(lsxContext);
- // instanciate PageNode
- if (stateCache != null && stateCache.nodeTree != null) {
- stateCache.nodeTree = stateCache.nodeTree.map((obj) => {
- return PageNode.instanciateFrom(obj);
- });
- }
- return stateCache;
- }
- /**
- * generate tree structure
- *
- * @param {string} rootPagePath
- * @param {Page[]} pages Array of Page model
- *
- * @memberOf Lsx
- */
- generatePageNodeTree(rootPagePath, pages) {
- const pathToNodeMap = {};
- pages.forEach((page) => {
- // add slash ensure not to forward match to another page
- // e.g. '/Java/' not to match to '/JavaScript'
- const pagePath = pathUtils.addTrailingSlash(page.path);
- // exclude rootPagePath itself
- if (this.isEquals(pagePath, rootPagePath)) {
- return;
- }
- const node = this.generatePageNode(pathToNodeMap, rootPagePath, pagePath); // this will not be null
- // set the Page substance
- node.page = page;
- });
- // return root objects
- const rootNodes = [];
- Object.keys(pathToNodeMap).forEach((pagePath) => {
- // exclude '/'
- if (pagePath === '/') {
- return;
- }
- const parentPath = this.getParentPath(pagePath);
- // pick up what parent doesn't exist
- if ((parentPath === '/') || !(parentPath in pathToNodeMap)) {
- rootNodes.push(pathToNodeMap[pagePath]);
- }
- });
- return rootNodes;
- }
- /**
- * generate PageNode instances for target page and the ancestors
- *
- * @param {any} pathToNodeMap
- * @param {any} rootPagePath
- * @param {any} pagePath
- * @returns
- * @memberof Lsx
- */
- generatePageNode(pathToNodeMap, rootPagePath, pagePath) {
- // exclude rootPagePath itself
- if (this.isEquals(pagePath, rootPagePath)) {
- return null;
- }
- // return when already registered
- if (pathToNodeMap[pagePath] != null) {
- return pathToNodeMap[pagePath];
- }
- // generate node
- const node = new PageNode(pagePath);
- pathToNodeMap[pagePath] = node;
- /*
- * process recursively for ancestors
- */
- // get or create parent node
- const parentPath = this.getParentPath(pagePath);
- const parentNode = this.generatePageNode(pathToNodeMap, rootPagePath, parentPath);
- // associate to patent
- if (parentNode != null) {
- parentNode.children.push(node);
- }
- return node;
- }
- /**
- * compare whether path1 and path2 is the same
- *
- * @param {string} path1
- * @param {string} path2
- * @returns
- *
- * @memberOf Lsx
- */
- isEquals(path1, path2) {
- return pathUtils.removeTrailingSlash(path1) === pathUtils.removeTrailingSlash(path2);
- }
- getParentPath(path) {
- return pathUtils.addTrailingSlash(decodeURIComponent(url.resolve(path, '../')));
- }
- renderContents() {
- const lsxContext = this.props.lsxContext;
- const {
- isLoading, isError, isCacheExists, nodeTree,
- } = this.state;
- if (isError) {
- return (
- <div className="text-warning">
- <i className="fa fa-exclamation-triangle fa-fw"></i>
- {lsxContext.tagExpression} (-> <small>{this.state.errorMessage}</small>)
- </div>
- );
- }
- return (
- <div className={isLoading ? 'lsx-blink' : ''}>
- { isLoading && (
- <div className="text-muted">
- <i className="fa fa-spinner fa-pulse mr-1"></i>
- {lsxContext.tagExpression}
- { isCacheExists && <small> (Showing cache..)</small> }
- </div>
- ) }
- { nodeTree && (
- <LsxListView nodeTree={this.state.nodeTree} lsxContext={this.props.lsxContext} />
- ) }
- </div>
- );
- }
- render() {
- return <div className="lsx">{this.renderContents()}</div>;
- }
- }
- Lsx.propTypes = {
- appContainer: PropTypes.object.isRequired,
- lsxContext: PropTypes.instanceOf(LsxContext).isRequired,
- forceToFetchData: PropTypes.bool,
- };
|