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

Merge branch 'feat/gw7839-remove-bookmark' into feat/gw7840-implement-rename-from-bookmark-sidebar

Mudana-Grune 3 лет назад
Родитель
Сommit
8058251864

+ 127 - 101
packages/app/src/components/Sidebar/Bookmarks.tsx

@@ -2,82 +2,87 @@
 import React, { useCallback, useEffect, useState } from 'react';
 
 import nodePath from 'path';
-
 import { DevidedPagePath, pathUtils } from '@growi/core';
-import PropTypes from 'prop-types';
 import { useTranslation } from 'react-i18next';
 import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 
 import { unbookmark } from '~/client/services/page-operation';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
-import { apiv3Put } from '~/client/util/apiv3-client';
-import LinkedPagePath from '~/models/linked-page-path';
-import { useSWRInifiniteBookmarkedPage } from '~/stores/bookmark';
-import { useCurrentUser } from '~/stores/context';
-
+import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
+import { IPageHasId } from '~/interfaces/page';
+import { useCurrentUser, useIsGuestUser } from '~/stores/context';
+import loggerFactory from '~/utils/logger';
 import ClosableTextInput, { AlertInfo, AlertType } from '../Common/ClosableTextInput';
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
-import PagePathHierarchicalLink from '../PagePathHierarchicalLink';
-
-import InfiniteScroll from './InfiniteScroll';
-
-const BookmarksItem = ({ data, swr }) : JSX.Element => {
-  const { t } = useTranslation('');
-  const { page } = data;
-  const dPagePath = new DevidedPagePath(page.path, false, true);
-  const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
-  const bookmarkItemId = `bookmark-item-${data._id}`;
-  const [isRenameInputShown, setRenameInputShown] = useState(false);
-
-  const bookmarkMenuItemClickHandler = useCallback(async() => {
-    await unbookmark(page.id);
-    swr.mutate();
-  }, [swr, page]);
-
-  const renameMenuItemClickHandler = useCallback(() => {
-    setRenameInputShown(true);
-  }, []);
-
-  const inputValidator = (title: string | null): AlertInfo | null => {
-    if (title == null || title === '' || title.trim() === '') {
-      return {
-        type: AlertType.WARNING,
-        message: t('form_validation.title_required'),
-      };
-    }
 
-    return null;
-  };
+const logger = loggerFactory('growi:BookmarkList');
+// TODO: Remove pagination and apply  scrolling (not infinity)
+const ACTIVE_PAGE = 1;
 
-  const onPressEnterForRenameHandler = useCallback(async(inputText: string) => {
-    const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(page.path ?? ''));
-    const newPagePath = nodePath.resolve(parentPath, inputText);
+type Props = {
+  pages: IPageHasId[] | undefined,
+  refreshBookmarkList: () => void
+}
 
-    if (newPagePath === page.path) {
-      setRenameInputShown(false);
-      return;
-    }
+const BookmarksItem = (props: Props) => {
+  const { t } = useTranslation();
+  const { pages, refreshBookmarkList } = props;
 
-    try {
-      setRenameInputShown(false);
-      await apiv3Put('/pages/rename', {
-        pageId: page._id,
-        revisionId: page.revision,
-        newPagePath,
-      });
-      swr.mutate();
-      toastSuccess(t('renamed_pages', { path: page.path }));
-    }
-    catch (err) {
-      setRenameInputShown(true);
-      toastError(err);
-    }
-  }, [swr, page, t]);
+  const generateBookmarkedPageList = pages?.map((page) => {
+    const dPagePath = new DevidedPagePath(page.path, false, true);
+    const { latter: pageTitle, former: formerPagePath } = dPagePath;
+    const bookmarkItemId = `bookmark-item-${page._id}`;
+    const [isRenameInputShown, setRenameInputShown] = useState(false);
 
-  return (
-    <>
-      <li className="list-group-item py-3 px-2" id={bookmarkItemId}>
-        <div className="d-flex w-100 justify-content-between">
+    const bookmarkMenuItemClickHandler = (async() => {
+      await unbookmark(page._id);
+      refreshBookmarkList();
+    });
+
+
+    const renameMenuItemClickHandler = () => {
+      setRenameInputShown(true);
+    };
+
+    const inputValidator = (title: string | null): AlertInfo | null => {
+      if (title == null || title === '' || title.trim() === '') {
+        return {
+          type: AlertType.WARNING,
+          message: t('form_validation.title_required'),
+        };
+      }
+
+      return null;
+    };
+
+    const onPressEnterForRenameHandler = useCallback(async(inputText: string) => {
+      const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(page.path ?? ''));
+      const newPagePath = nodePath.resolve(parentPath, inputText);
+
+      if (newPagePath === page.path) {
+        setRenameInputShown(false);
+        return;
+      }
+
+      try {
+        setRenameInputShown(false);
+        await apiv3Put('/pages/rename', {
+          pageId: page._id,
+          revisionId: page.revision,
+          newPagePath,
+        });
+        refreshBookmarkList();
+        toastSuccess(t('renamed_pages', { path: page.path }));
+      }
+      catch (err) {
+        setRenameInputShown(true);
+        toastError(err);
+      }
+    }, [page, t]);
+
+    return (
+      <div className="d-flex justify-content-between" key={page._id}>
+        <li className="list-group-item list-group-item-action border-0 py-0 pr-3 d-flex align-items-center" id={bookmarkItemId}>
           { isRenameInputShown ? (
             <ClosableTextInput
               value={nodePath.basename(page.path ?? '')}
@@ -87,77 +92,98 @@ const BookmarksItem = ({ data, swr }) : JSX.Element => {
               inputValidator={inputValidator}
             />
           ) : (
-            <h5 className="my-0">
-              <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
-            </h5>
+            <a href={`/${page._id}`} className="grw-bookmarks-title-anchor flex-grow-1">
+              <p className={`text-truncate m-auto ${page.isEmpty && 'grw-sidebar-text-muted'}`}>{pageTitle}</p>
+            </a>
           )}
-
           <PageItemControl
             pageId={page._id}
             isEnableActions
             forceHideMenuItems={[MenuItemType.DUPLICATE]}
             onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
-            onClickRenameMenuItem={renameMenuItemClickHandler}
           >
             <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0 grw-visible-on-hover mr-1">
               <i className="icon-options fa fa-rotate-90 p-1"></i>
             </DropdownToggle>
           </PageItemControl>
-
           <UncontrolledTooltip
             modifiers={{ preventOverflow: { boundariesElement: 'window' } }}
             autohide={false}
-            placement="left"
+            placement="right"
             target={bookmarkItemId}
           >
-            {page.path}
+            { formerPagePath || '/' }
           </UncontrolledTooltip>
-        </div>
-      </li>
+        </li>
+      </div>
+    );
+  });
+
 
+  return (
+    <>
+      <ul className="grw-bookmarks-list list-group p-3">
+        <div className="grw-bookmarks-item-container">
+          {generateBookmarkedPageList}
+        </div>
+      </ul>
     </>
   );
-
 };
 
-BookmarksItem.propTypes = {
-  data: PropTypes.any,
-  swr: PropTypes.any,
-};
 
 const Bookmarks = () : JSX.Element => {
-  const { t } = useTranslation('');
+  const { t } = useTranslation();
   const { data: currentUser } = useCurrentUser();
-  const swr = useSWRInifiniteBookmarkedPage(currentUser?._id);
-  const isEmpty = swr.data?.[0].docs.length === 0;
-  const isReachingEnd = isEmpty || (swr.data && swr.data[0].nextPage == null);
+  const { data: isGuestUser } = useIsGuestUser();
+  const [pages, setPages] = useState<IPageHasId[]>([]);
+  const page = ACTIVE_PAGE;
+
+  const getMyBookmarkList = useCallback(async() => {
+    try {
+      const res = await apiv3Get(`/bookmarks/${currentUser?._id}`, { page });
+      const { paginationResult } = res.data;
+      setPages(paginationResult.docs.map((page) => {
+        return {
+          ...page.page,
+        };
+      }));
+    }
+    catch (error) {
+      logger.error('failed to fetch data', error);
+      toastError(error, 'Error occurred in bookmark page list');
+    }
+  }, [currentUser, page]);
 
   useEffect(() => {
-    swr.mutate();
-  }, []);
+    getMyBookmarkList();
+  }, [getMyBookmarkList]);
+
+  const renderBookmarksItem = () => {
+    if (pages?.length === 0) {
+      return (
+        <h3 className="pl-3">
+          { t('No bookmarks yet') }
+        </h3>
+      );
+    }
+    return <BookmarksItem pages={pages} refreshBookmarkList={getMyBookmarkList} />;
+  };
 
   return (
     <>
       <div className="grw-sidebar-content-header p-3">
         <h3 className="mb-0">{t('Bookmarks')}</h3>
       </div>
-      <div className="grw-bookmarks-list p-3">
-        {isEmpty ? t('No bookmarks yet') : (
-          <div>
-            <ul className="list-group list-group-flush">
-              <InfiniteScroll
-                swrInifiniteResponse={swr}
-                isReachingEnd={isReachingEnd}
-              >
-                {paginationResult => paginationResult?.docs.map(data => (
-                  <BookmarksItem key={data._id} data={data} swr={swr} />
-                ))
-                }
-              </InfiniteScroll>
-            </ul>
-          </div>
-        )}
-      </div>
+
+      { isGuestUser
+        ? (
+          <h3 className="pl-3">
+            { t('Not available for guest') }
+          </h3>
+        ) : renderBookmarksItem()
+      }
+
     </>
   );
 

+ 38 - 0
packages/app/src/styles/_bookmarks.scss

@@ -0,0 +1,38 @@
+$grw-sidebar-content-header-height: 58px;
+$grw-sidebar-content-footer-height: 50px;
+
+.grw-bookmarks-list {
+  min-height: calc(100vh - ($grw-navbar-height + $grw-navbar-border-width + $grw-sidebar-content-header-height + $grw-sidebar-content-footer-height));
+
+  .btn-page-item-control {
+    .icon-plus::before {
+      font-size: 18px;
+    }
+  }
+
+  .list-group-item {
+    .grw-visible-on-hover {
+      display: none;
+    }
+
+    &:hover {
+      .grw-visible-on-hover {
+        display: block;
+      }
+    }
+
+    .grw-bookmarks-title-anchor {
+      width: 100%;
+      overflow: hidden;
+      text-decoration: none;
+    }
+
+  }
+  .grw-bookmarks-item-container {
+    .list-group-item {
+      min-width: 35px;
+      height: 40px;
+    }
+  }
+
+}

+ 1 - 0
packages/app/src/styles/style-app.scss

@@ -36,6 +36,7 @@
 // growi component
 @import 'admin';
 @import 'attachments';
+@import 'bookmarks';
 @import 'comment';
 @import 'comment_growi';
 @import 'drawio';

+ 13 - 0
packages/app/src/styles/theme/_apply-colors-dark.scss

@@ -300,6 +300,19 @@ ul.pagination {
       box-shadow: none !important;
     }
   }
+
+  // bookmarks
+  .grw-bookmarks-list {
+    @include override-list-group-item-for-pagetree(
+      $color-sidebar-context,
+      lighten($bgcolor-sidebar-context, 8%),
+      lighten($bgcolor-sidebar-context, 15%),
+      darken($color-sidebar-context, 15%),
+      darken($color-sidebar-context, 10%),
+      lighten($bgcolor-sidebar-context, 18%),
+      lighten($bgcolor-sidebar-context, 24%)
+    );
+  }
   .private-legacy-pages-link {
     &:hover {
       background: $bgcolor-list-hover;

+ 13 - 0
packages/app/src/styles/theme/_apply-colors-light.scss

@@ -198,6 +198,19 @@ $dropdown-link-active-bg: $bgcolor-dropdown-link-active;
       @include button-outline-svg-icon-variant($gray-400, $primary);
     }
   }
+
+  // bookmark
+  .grw-bookmarks-list {
+    @include override-list-group-item-for-pagetree(
+      $color-sidebar-context,
+      darken($bgcolor-sidebar-context, 5%),
+      darken($bgcolor-sidebar-context, 12%),
+      lighten($color-sidebar-context, 10%),
+      lighten($color-sidebar-context, 8%),
+      darken($bgcolor-sidebar-context, 15%),
+      darken($bgcolor-sidebar-context, 24%)
+    );
+  }
   .private-legacy-pages-link {
     &:hover {
       background: $bgcolor-list-hover;

+ 0 - 15
packages/app/src/styles/theme/_apply-colors.scss

@@ -319,21 +319,6 @@ ul.pagination {
     }
   }
 
-  .grw-bookmarks-list {
-    .list-group {
-      .list-group-item {
-        background-color: transparent;
-        &:hover{
-          background-color: $bgcolor-list-hover;
-        }
-      }
-    }
-    .list-group-flush {
-      .list-group-item {
-        border: none;
-      }
-    }
-  }
 }
 
 /*