Просмотр исходного кода

Merge pull request #4686 from weseek/imprv/normalize-isopen-istarget

imprv: Normalize isopen istarget
Haku Mizuki 4 лет назад
Родитель
Сommit
bc03dc5d27

+ 3 - 1
packages/app/src/client/services/ContextExtractor.tsx

@@ -5,7 +5,7 @@ import {
   useCreatedAt, useDeleteUsername, useDeletedAt, useHasChildren, useHasDraftOnHackmd, useIsAbleToDeleteCompletely,
   useIsDeletable, useIsDeleted, useIsNotCreatable, useIsPageExist, useIsTrashPage, useIsUserPage, useLastUpdateUsername,
   usePageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
-  useShareLinkId, useShareLinksNumber, useTemplateTagData, useUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser,
+  useShareLinkId, useShareLinksNumber, useTemplateTagData, useUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
 } from '../../stores/context';
 import {
   useEditorMode, useIsDeviceSmallerThanMd, usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser,
@@ -53,6 +53,7 @@ const ContextExtractor: FC = () => {
   const hasDraftOnHackmd = !!mainContent?.getAttribute('data-page-has-draft-on-hackmd');
   const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
   const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
+  const targetAndAncestors = JSON.parse(mainContent?.getAttribute('data-target-and-ancestors') || jsonNull);
 
   /*
    * use static swr
@@ -93,6 +94,7 @@ const ContextExtractor: FC = () => {
   useUpdatedAt(updatedAt);
   useCreator(creator);
   useRevisionAuthor(revisionAuthor);
+  useTargetAndAncestors(targetAndAncestors);
 
   return (
     <div>

+ 2 - 1
packages/app/src/components/Sidebar/CustomSidebar.tsx

@@ -6,6 +6,7 @@ import { useSWRxPageByPath } from '~/stores/page';
 
 import { withUnstatedContainers } from '../UnstatedUtils';
 import RevisionRenderer from '../Page/RevisionRenderer';
+import { IRevision } from '~/interfaces/revision';
 
 const logger = loggerFactory('growi:cli:CustomSidebar');
 
@@ -33,7 +34,7 @@ const CustomSidebar: FC<Props> = (props: Props) => {
   const { data: page, mutate } = useSWRxPageByPath('/Sidebar');
 
   const isLoading = page === undefined;
-  const markdown = page?.revision?.body;
+  const markdown = (page?.revision as IRevision)?.body;
 
   return (
     <>

+ 51 - 16
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -1,7 +1,11 @@
-import React, { useCallback, useState, FC } from 'react';
+import React, {
+  useCallback, useState, FC, useEffect,
+} from 'react';
+import nodePath from 'path';
 
 import { ItemNode } from './ItemNode';
 import { useSWRxPageChildren } from '../../../stores/page-listing';
+import { usePageId } from '../../../stores/context';
 
 
 interface ItemProps {
@@ -9,15 +13,27 @@ interface ItemProps {
   isOpen?: boolean
 }
 
+// Utility to mark target
+const markTarget = (children: ItemNode[], targetId: string): void => {
+  children.forEach((node) => {
+    if (node.page._id === targetId) {
+      node.page.isTarget = true;
+    }
+    return node;
+  });
+
+  return;
+};
+
 const Item: FC<ItemProps> = (props: ItemProps) => {
   const { itemNode, isOpen: _isOpen = false } = props;
 
   const { page, children } = itemNode;
 
   const [currentChildren, setCurrentChildren] = useState(children);
-
   const [isOpen, setIsOpen] = useState(_isOpen);
 
+  const { data: targetId } = usePageId();
   const { data, error } = useSWRxPageChildren(isOpen ? page._id : null);
 
   const hasChildren = useCallback((): boolean => {
@@ -28,28 +44,47 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
     setIsOpen(!isOpen);
   }, [isOpen]);
 
+  // didMount
+  useEffect(() => {
+    if (hasChildren()) setIsOpen(true);
+  }, []);
+
   /*
-   * When swr fetch succeeded
+   * Make sure itemNode.children and currentChildren are synced
    */
-  if (isOpen && error == null && data != null) {
-    const { children } = data;
-    itemNode.children = ItemNode.generateNodesFromPages(children);
-  }
+  useEffect(() => {
+    if (children.length > currentChildren.length) {
+      markTarget(children, targetId);
+      setCurrentChildren(children);
+    }
+  }, []);
 
-  // make sure itemNode.children and currentChildren are synced
-  if (children.length > currentChildren.length) {
-    setCurrentChildren(children);
-  }
+  /*
+   * When swr fetch succeeded
+   */
+  useEffect(() => {
+    if (isOpen && error == null && data != null) {
+      const newChildren = ItemNode.generateNodesFromPages(data.children);
+      markTarget(newChildren, targetId);
+      setCurrentChildren(newChildren);
+    }
+  }, [data]);
 
   // TODO: improve style
-  const style = { margin: '10px', opacity: 1.0 };
-  if (page.isTarget) style.opacity = 0.7;
+  const opacityStyle = { opacity: 1.0 };
+  if (page.isTarget) opacityStyle.opacity = 0.7;
+  if (isOpen) opacityStyle.opacity = 0.5;
 
   return (
-    <div style={style}>
-      <p><button type="button" className="btn btn-light p-1" onClick={onClickLoadChildren}>Load</button>  {page.path}</p>
+    <div style={{ margin: '10px' }}>
+      <div style={opacityStyle}>
+        <button type="button" className="d-inline-block btn btn-light p-1 mr-1" onClick={onClickLoadChildren}>Load</button>
+        <a href={page._id} className="d-inline-block">
+          <p>{nodePath.basename(page.path as string) || '/'}</p>
+        </a>
+      </div>
       {
-        hasChildren() && currentChildren.map(node => (
+        isOpen && hasChildren() && currentChildren.map(node => (
           <Item
             key={node.page._id}
             itemNode={node}

+ 3 - 4
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -4,7 +4,7 @@ import { IPage } from '../../../interfaces/page';
 import { ItemNode } from './ItemNode';
 import Item from './Item';
 import { useSWRxPageAncestorsChildren } from '../../../stores/page-listing';
-import { useTargetAndAncestors } from '../../../stores/context';
+import { useTargetAndAncestors, useCurrentPagePath } from '../../../stores/context';
 import { HasObjectId } from '../../../interfaces/has-object-id';
 
 
@@ -47,12 +47,11 @@ const generateInitialNodeAfterResponse = (ancestorsChildren: Record<string, Part
  * ItemsTree
  */
 const ItemsTree: FC = () => {
-  // TODO: get from static SWR
-  const path = '/Sandbox';
+  const { data: currentPath } = useCurrentPagePath();
 
   const { data, error } = useTargetAndAncestors();
 
-  const { data: ancestorsChildrenData, error: error2 } = useSWRxPageAncestorsChildren(path);
+  const { data: ancestorsChildrenData, error: error2 } = useSWRxPageAncestorsChildren(currentPath || null);
 
   if (error != null || error2 != null) {
     return null;

+ 2 - 2
packages/app/src/stores/page-listing.tsx

@@ -5,10 +5,10 @@ import { AncestorsChildrenResult, ChildrenResult } from '../interfaces/page-list
 
 
 export const useSWRxPageAncestorsChildren = (
-    path: string,
+    path: string | null,
 ): SWRResponse<AncestorsChildrenResult, Error> => {
   return useSWR(
-    `/page-listing/ancestors-children?path=${path}`,
+    path ? `/page-listing/ancestors-children?path=${path}` : null,
     endpoint => apiv3Get(endpoint).then((response) => {
       return {
         ancestorsChildren: response.data.ancestorsChildren,