Taichi Masuyama 4 лет назад
Родитель
Сommit
57099afd31

+ 15 - 29
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -14,7 +14,7 @@ const { isTopPage } = pagePathUtils;
  */
  */
 const generateInitialNode = (targetAndAncestors: Partial<IPage>[]): ItemNode => {
 const generateInitialNode = (targetAndAncestors: Partial<IPage>[]): ItemNode => {
   const rootPage = targetAndAncestors[targetAndAncestors.length - 1]; // the last item is the root
   const rootPage = targetAndAncestors[targetAndAncestors.length - 1]; // the last item is the root
-  if (!isTopPage(rootPage?.path as string)) throw Error('/ not exist in ancestors');
+  if (!isTopPage(rootPage?.path as string)) throw new Error('/ not exist in ancestors');
 
 
   const nodes = targetAndAncestors.map((page): ItemNode => {
   const nodes = targetAndAncestors.map((page): ItemNode => {
     return new ItemNode(page, []);
     return new ItemNode(page, []);
@@ -29,32 +29,12 @@ const generateInitialNode = (targetAndAncestors: Partial<IPage>[]): ItemNode =>
   return rootNode;
   return rootNode;
 };
 };
 
 
-const generateInitialNodeWithChildren = (ancestors: Partial<IPage>[], ancestorsChildren: Record<string, Partial<IPage>[]>): ItemNode => {
-  // create nodes
-  const nodes = ancestors.map((page): ItemNode => {
-    const children = ancestorsChildren[page.path as string].map(page => new ItemNode(page, []));
-    return new ItemNode(page, children);
-  });
-
-  // update children for each node
-  const rootNode = nodes.reduce((child, parent) => {
-    parent.children = [child];
-    return parent;
-  });
-
-  return rootNode;
-};
-
-// TODO: get from props
-const path = '/Sandbox/Bootstrap4';
-const id = '6181188ae38676152e464fc2';
-
 /*
 /*
  * ItemsTree
  * ItemsTree
  */
  */
 const ItemsTree: FC = () => {
 const ItemsTree: FC = () => {
   // TODO: get from props
   // TODO: get from props
-  const path = '/Sandbox/Bootstrap4';
+  const path = '/';
 
 
   const { data: targetAndAncestors, error } = useTargetAndAncestors();
   const { data: targetAndAncestors, error } = useTargetAndAncestors();
 
 
@@ -78,16 +58,22 @@ const ItemsTree: FC = () => {
     const { ancestorsChildren } = ancestorsChildrenData;
     const { ancestorsChildren } = ancestorsChildrenData;
 
 
     // flatten ancestors
     // flatten ancestors
-    const partialChildren: ItemNode[] = [];
-    let currentNode = initialNode;
-    while (currentNode.hasChildren() && currentNode?.children?.[0] != null) {
-      const child = currentNode.children[0];
-      partialChildren.push(child);
-      currentNode = child;
+    let ancestors: ItemNode[] = [];
+
+    if (initialNode.children.length === 0) { // when showing top page
+      ancestors = [initialNode];
+    }
+    else {
+      let currentNode = initialNode;
+      while (currentNode.hasChildren() && currentNode?.children?.[0] != null) {
+        ancestors.push(currentNode);
+        const child = currentNode.children[0];
+        currentNode = child;
+      }
     }
     }
 
 
     // update children
     // update children
-    partialChildren.forEach((node) => {
+    ancestors.forEach((node) => {
       const childPages = ancestorsChildren[node.page.path as string];
       const childPages = ancestorsChildren[node.page.path as string];
       node.children = ItemNode.generateNodesFromPages(childPages);
       node.children = ItemNode.generateNodesFromPages(childPages);
     });
     });

+ 27 - 6
packages/app/src/server/models/page.ts

@@ -84,6 +84,14 @@ schema.plugin(uniqueValidator);
  * Methods
  * Methods
  */
  */
 const collectAncestorPaths = (path: string, ancestorPaths: string[] = []): string[] => {
 const collectAncestorPaths = (path: string, ancestorPaths: string[] = []): string[] => {
+  if (isTopPage(path)) return ['/'];
+
+  const parentPath = nodePath.dirname(path);
+  ancestorPaths.push(parentPath);
+  return collectAncestorPaths(parentPath, ancestorPaths);
+};
+
+const collectAncestorPathsExcludingTop = (path: string, ancestorPaths: string[] = []): string[] => {
   if (isTopPage(path)) return ancestorPaths;
   if (isTopPage(path)) return ancestorPaths;
 
 
   const parentPath = nodePath.dirname(path);
   const parentPath = nodePath.dirname(path);
@@ -98,7 +106,7 @@ const hasSlash = (str: string): boolean => {
 /*
 /*
  * Generate RE2 instance for one level lower path
  * Generate RE2 instance for one level lower path
  */
  */
-const generateChildrenRegExp = (path: string): RE2 => {
+const generateChildrenRE2 = (path: string): RE2 => {
   // https://regex101.com/r/laJGzj/1
   // https://regex101.com/r/laJGzj/1
   // ex. /any_level1
   // ex. /any_level1
   if (isTopPage(path)) return new RE2(/^\/[^/]+$/);
   if (isTopPage(path)) return new RE2(/^\/[^/]+$/);
@@ -108,6 +116,19 @@ const generateChildrenRegExp = (path: string): RE2 => {
   return new RE2(`^${path}(\\/[^/]+)\\/?$`);
   return new RE2(`^${path}(\\/[^/]+)\\/?$`);
 };
 };
 
 
+/*
+ * Generate RegExp instance for one level lower path
+ */
+const generateChildrenRegExp = (path: string): RegExp => {
+  // https://regex101.com/r/laJGzj/1
+  // ex. /any_level1
+  if (isTopPage(path)) return new RegExp(/^\/[^/]+$/);
+
+  // https://regex101.com/r/mrDJrx/1
+  // ex. /parent/any_child OR /any_level1
+  return new RegExp(`^${path}(\\/[^/]+)\\/?$`);
+};
+
 /*
 /*
  * Create empty pages if the page in paths didn't exist
  * Create empty pages if the page in paths didn't exist
  */
  */
@@ -149,7 +170,7 @@ schema.statics.getParentIdAndFillAncestors = async function(path: string): Promi
     return parent._id;
     return parent._id;
   }
   }
 
 
-  const ancestorPaths = collectAncestorPaths(path); // paths of parents need to be created
+  const ancestorPaths = collectAncestorPathsExcludingTop(path); // paths of parents need to be created
 
 
   // just create ancestors with empty pages
   // just create ancestors with empty pages
   await this.createEmptyPagesByPaths(ancestorPaths);
   await this.createEmptyPagesByPaths(ancestorPaths);
@@ -235,7 +256,7 @@ schema.statics.findTargetAndAncestorsByPathOrId = async function(pathOrId: strin
     path = pathOrId;
     path = pathOrId;
   }
   }
 
 
-  const ancestorPaths = collectAncestorPaths(path);
+  const ancestorPaths = collectAncestorPathsExcludingTop(path);
   ancestorPaths.push(path); // include target
   ancestorPaths.push(path); // include target
 
 
   // Do not populate
   // Do not populate
@@ -263,7 +284,7 @@ schema.statics.findChildrenByParentPathOrIdAndViewer = async function(parentPath
   let queryBuilder: PageQueryBuilder;
   let queryBuilder: PageQueryBuilder;
   if (hasSlash(parentPathOrId)) {
   if (hasSlash(parentPathOrId)) {
     const path = parentPathOrId;
     const path = parentPathOrId;
-    const regexp = generateChildrenRegExp(path);
+    const regexp = generateChildrenRE2(path);
     queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source } }));
     queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source } }));
   }
   }
   else {
   else {
@@ -277,7 +298,7 @@ schema.statics.findChildrenByParentPathOrIdAndViewer = async function(parentPath
 
 
 schema.statics.findAncestorsChildrenByPathAndViewer = async function(path: string, user, userGroups = null): Promise<Record<string, PageDocument[]>> {
 schema.statics.findAncestorsChildrenByPathAndViewer = async function(path: string, user, userGroups = null): Promise<Record<string, PageDocument[]>> {
   const ancestorPaths = collectAncestorPaths(path);
   const ancestorPaths = collectAncestorPaths(path);
-  const regexps = ancestorPaths.map(path => new RegExp(generateChildrenRegExp(path).source)); // cannot use re2
+  const regexps = ancestorPaths.map(path => new RegExp(generateChildrenRegExp(path))); // cannot use re2
 
 
   // get pages at once
   // get pages at once
   const queryBuilder = new PageQueryBuilder(this.find({ path: { $in: regexps } }));
   const queryBuilder = new PageQueryBuilder(this.find({ path: { $in: regexps } }));
@@ -293,7 +314,7 @@ schema.statics.findAncestorsChildrenByPathAndViewer = async function(path: strin
   // make map
   // make map
   const pathToChildren: Record<string, PageDocument[]> = {};
   const pathToChildren: Record<string, PageDocument[]> = {};
   ancestorPaths.forEach((path) => {
   ancestorPaths.forEach((path) => {
-    pathToChildren[path] = pages.filter(page => page.path === path);
+    pathToChildren[path] = pages.filter(page => nodePath.dirname(page.path) === path);
   });
   });
 
 
   return pathToChildren;
   return pathToChildren;

+ 1 - 1
packages/app/src/stores/context.tsx

@@ -4,5 +4,5 @@ import { useStaticSWR } from './use-static-swr';
 import { TargetAndAncestors } from '../interfaces/page-listing-results';
 import { TargetAndAncestors } from '../interfaces/page-listing-results';
 
 
 export const useTargetAndAncestors = (initialData?: TargetAndAncestors): SWRResponse<TargetAndAncestors, Error> => {
 export const useTargetAndAncestors = (initialData?: TargetAndAncestors): SWRResponse<TargetAndAncestors, Error> => {
-  return useStaticSWR<TargetAndAncestors, Error>('targetAndAncestors', initialData);
+  return useStaticSWR<TargetAndAncestors, Error>('swr-targetAndAncestors', null, initialData);
 };
 };

+ 1 - 2
packages/app/src/utils/swr-utils.ts

@@ -1,9 +1,8 @@
 import { SWRConfiguration } from 'swr';
 import { SWRConfiguration } from 'swr';
 
 
-import axios from './axios';
 
 
 export const swrGlobalConfiguration: SWRConfiguration = {
 export const swrGlobalConfiguration: SWRConfiguration = {
-  fetcher: url => axios.get(url).then(res => res.data),
+  fetcher: null,
   revalidateOnFocus: false,
   revalidateOnFocus: false,
   errorRetryCount: 1,
   errorRetryCount: 1,
 };
 };