Explorar el Código

Fix BookmarksItem component

https://youtrack.weseek.co.jp/issue/GW-7832
- Add type definition for BookmarkItem props
- Update useTranslation
- Update generation of bookmarked page list
- Move UncontrolledTooltip component to BookmarkItem
- Update page link and title
- Update tooltip content with former link
- Remove implementation of InfiniteScroll and SWRInfinite
- Add rendering condition for guestUser
- Update get user bookmarks route
- Create styling for bookmarks
- Add bookmarks styling for dark/light mode color
Mudana-Grune hace 3 años
padre
commit
52d77ac287

+ 92 - 52
packages/app/src/components/Sidebar/Bookmarks.tsx

@@ -1,79 +1,119 @@
 
-import React from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 
 import { DevidedPagePath } from '@growi/core';
-import PropTypes from 'prop-types';
 import { useTranslation } from 'react-i18next';
 import { UncontrolledTooltip } from 'reactstrap';
 
-import LinkedPagePath from '~/models/linked-page-path';
-import { useSWRInifiniteBookmarkedPage } from '~/stores/bookmark';
-import { useCurrentUser } from '~/stores/context';
+import { toastError } from '~/client/util/apiNotification';
+import { apiv3Get } from '~/client/util/apiv3-client';
+import { IPageHasId } from '~/interfaces/page';
+import { useCurrentUser, useIsGuestUser } from '~/stores/context';
+import loggerFactory from '~/utils/logger';
 
-import PagePathHierarchicalLink from '../PagePathHierarchicalLink';
+const logger = loggerFactory('growi:BookmarkList');
 
-import InfiniteScroll from './InfiniteScroll';
+// TODO: Adjust pagination
+const ACTIVE_PAGE = 1;
 
+type Props = {
+  pages: IPageHasId[]
+}
 
-const BookmarksItem = ({ data }) => {
-  const { page } = data;
-  const dPagePath = new DevidedPagePath(page.path, false, true);
-  const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
+const BookmarksItem = (props: Props) => {
+  const { pages } = props;
 
-  return (
-    <li className="list-group-item py-3 px-0" id={`bookmark-item-${data._id}`}>
-      <div className="d-flex w-100">
-        <h5 className="my-0">
-          <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
-        </h5>
+  const generateBookmarkedPageList = pages.map((page) => {
+    const dPagePath = new DevidedPagePath(page.path, false, true);
+    const { latter: pageTitle, former: formerPagePath } = dPagePath;
+    return (
+      <div key={page._id}>
+        <li className="list-group-item list-group-item-action border-0 py-0 pr-3 d-flex align-items-center" id={`bookmark-item-${page._id}`}>
+          <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>
+        </li>
+        <UncontrolledTooltip
+          modifiers={{ preventOverflow: { boundariesElement: 'window' } }}
+          autohide={false}
+          placement="right"
+          target={`bookmark-item-${page._id}`}
+        >
+          { formerPagePath || '/' }
+        </UncontrolledTooltip>
       </div>
-    </li>
-  );
+    );
+  });
 
-};
 
-BookmarksItem.propTypes = {
-  data: PropTypes.any,
+  return (
+    <>
+      <ul className="grw-bookmarks-list list-group p-3">
+        <div className="grw-bookmarks-item-container">
+          {generateBookmarkedPageList}
+        </div>
+      </ul>
+    </>
+  );
 };
 
+
 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 getMyBookmarkList = useCallback(async() => {
+    // TODO: Adjust pagination
+    const page = ACTIVE_PAGE;
+
+    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]);
+
+  useEffect(() => {
+    getMyBookmarkList();
+  }, [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} />
-                    <UncontrolledTooltip
-                      modifiers={{ preventOverflow: { boundariesElement: 'window' } }}
-                      autohide={false}
-                      placement="right"
-                      target={`bookmark-item-${data._id}`}
-                    >
-                      {data.page.path}
-                    </UncontrolledTooltip>
-                  </>
-                ))
-                }
-              </InfiniteScroll>
-            </ul>
-          </div>
-        )}
-      </div>
+
+      { isGuestUser
+        ? (
+          <h3 className="pl-3">
+            { t('Not available for guest') }
+          </h3>
+        )
+        : (
+          <>
+            { pages.length === 0
+              ? (
+                <h3 className="pl-3">
+                  { t('No bookmarks yet') }
+                </h3>
+              )
+              : (
+                <BookmarksItem pages={pages} />
+              )
+            }
+          </>
+        )
+      }
     </>
   );
 

+ 0 - 2
packages/app/src/server/routes/apiv3/bookmarks.js

@@ -201,7 +201,6 @@ module.exports = (crowi) => {
     const { userId } = req.params;
     const page = req.query.page;
     const limit = parseInt(req.query.limit) || await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM') || 30;
-    const offset = page > 0 ? (page - 1) * limit : page;
 
     if (userId == null) {
       return res.apiv3Err('User id is not found or forbidden', 400);
@@ -223,7 +222,6 @@ module.exports = (crowi) => {
               model: 'User',
             },
           },
-          offset,
           page,
           limit,
         },

+ 0 - 15
packages/app/src/stores/bookmark.ts

@@ -1,6 +1,5 @@
 import { SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
-import useSWRInfinite, { SWRInfiniteResponse } from 'swr/infinite';
 
 import { apiv3Get } from '../client/util/apiv3-client';
 import { IBookmarkInfo } from '../interfaces/bookmark-info';
@@ -18,17 +17,3 @@ export const useSWRBookmarkInfo = (pageId: string | null | undefined): SWRRespon
     }),
   );
 };
-
-export const useSWRInifiniteBookmarkedPage = (userId: string | null | undefined) : SWRInfiniteResponse => {
-  const getKey = (page: number) => {
-    return userId != null ? `/bookmarks/${userId}/?page=${page + 1}` : null;
-  };
-  return useSWRInfinite(
-    getKey,
-    (endpoint: string) => apiv3Get<{ paginationResult }>(endpoint).then(response => response.data?.paginationResult),
-    {
-      revalidateFirstPage: false,
-      revalidateAll: false,
-    },
-  );
-};

+ 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 - 4
packages/app/src/styles/theme/_apply-colors.scss

@@ -20,7 +20,6 @@ $bgcolor-page-list-group-item-active: lighten($primary, 76%) !default;
 $color-page-list-group-item-meta: $gray-500 !default;
 $color-search-page-list-title: $color-global !default;
 $bgcolor-subnav: darken($bgcolor-global, 3%) !default;
-$bgcolor-list-hover: darken($primary, 8%) !default;
 
 // override bootstrap variables
 $body-bg: $bgcolor-global;
@@ -323,9 +322,6 @@ ul.pagination {
     .list-group {
       .list-group-item {
         background-color: transparent;
-        &:hover{
-          background-color: $bgcolor-list-hover;
-        }
       }
     }
     .list-group-flush {