Browse Source

Add bookmark folder button to Sidebar

https://youtrack.weseek.co.jp/issue/GW-7895
- Create bookmark folder component
- Implement ClosableTextInput to BookmarkFolder Component
- Create SWR to fetch users bookmark folder
- Store bookmark folder from Bookmark component
Mudana-Grune 3 years ago
parent
commit
cb4f46626b

+ 42 - 4
packages/app/src/components/Sidebar/Bookmarks.tsx

@@ -1,25 +1,31 @@
 
 
-import React from 'react';
+import React, { useState } from 'react';
 
 
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
-import { toastSuccess } from '~/client/util/apiNotification';
+import { toastSuccess, toastError } from '~/client/util/apiNotification';
+import { apiv3Post } from '~/client/util/apiv3-client';
 import { IPageToDeleteWithMeta } from '~/interfaces/page';
 import { IPageToDeleteWithMeta } from '~/interfaces/page';
 import { OnDeletedFunction } from '~/interfaces/ui';
 import { OnDeletedFunction } from '~/interfaces/ui';
-import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark';
+import { useSWRxCurrentUserBookmarkFolders, useSWRxCurrentUserBookmarks } from '~/stores/bookmark';
 import { useIsGuestUser } from '~/stores/context';
 import { useIsGuestUser } from '~/stores/context';
 import { usePageDeleteModal } from '~/stores/modal';
 import { usePageDeleteModal } from '~/stores/modal';
 
 
 
 
+import BookmarkFolder from './Bookmarks/BookmarkFolder';
 import BookmarkItem from './Bookmarks/BookmarkItem';
 import BookmarkItem from './Bookmarks/BookmarkItem';
 
 
-
 const Bookmarks = () : JSX.Element => {
 const Bookmarks = () : JSX.Element => {
   const { t } = useTranslation();
   const { t } = useTranslation();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: currentUserBookmarksData, mutate: mutateCurrentUserBookmarks } = useSWRxCurrentUserBookmarks();
   const { data: currentUserBookmarksData, mutate: mutateCurrentUserBookmarks } = useSWRxCurrentUserBookmarks();
+  const { data: currentUserBookmarkFolder, mutate: mutateCurrentUserBookmarkFolder } = useSWRxCurrentUserBookmarkFolders();
   const { open: openDeleteModal } = usePageDeleteModal();
   const { open: openDeleteModal } = usePageDeleteModal();
 
 
+  const [isRenameFolderShown, setIsRenameFolderShown] = useState<boolean>(false);
+  const [folderName, setFolderName] = useState<string>('');
+  const [currentParentFolder, setCurrentParentFolder] = useState(null);
+
   const deleteMenuItemClickHandler = (pageToDelete: IPageToDeleteWithMeta) => {
   const deleteMenuItemClickHandler = (pageToDelete: IPageToDeleteWithMeta) => {
     const pageDeletedHandler : OnDeletedFunction = (pathOrPathsToDelete, _isRecursively, isCompletely) => {
     const pageDeletedHandler : OnDeletedFunction = (pathOrPathsToDelete, _isRecursively, isCompletely) => {
       if (typeof pathOrPathsToDelete !== 'string') {
       if (typeof pathOrPathsToDelete !== 'string') {
@@ -38,6 +44,25 @@ const Bookmarks = () : JSX.Element => {
     openDeleteModal([pageToDelete], { onDeleted: pageDeletedHandler });
     openDeleteModal([pageToDelete], { onDeleted: pageDeletedHandler });
   };
   };
 
 
+  const onPressEnterHandler = async(folderName: string) => {
+    setFolderName(folderName);
+    try {
+      await apiv3Post('/bookmark-folder', { name: folderName, parent: currentParentFolder });
+      setIsRenameFolderShown(false);
+      setFolderName('');
+      mutateCurrentUserBookmarkFolder();
+      toastSuccess(t('Create New Bookmark Folder Success'));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  };
+
+  const onClickOutsideHandler = () => {
+    setIsRenameFolderShown(false);
+    setFolderName('');
+  };
+
   const renderBookmarkList = () => {
   const renderBookmarkList = () => {
     if (currentUserBookmarksData?.length === 0) {
     if (currentUserBookmarksData?.length === 0) {
       return (
       return (
@@ -70,6 +95,19 @@ const Bookmarks = () : JSX.Element => {
       <div className="grw-sidebar-content-header p-3">
       <div className="grw-sidebar-content-header p-3">
         <h3 className="mb-0">{t('Bookmarks')}</h3>
         <h3 className="mb-0">{t('Bookmarks')}</h3>
       </div>
       </div>
+      {!isGuestUser && (
+        <>
+          <BookmarkFolder
+            onClickNewFolder={() => setIsRenameFolderShown(true)}
+            isRenameInputShown={isRenameFolderShown}
+            onClickOutside={onClickOutsideHandler}
+            onPressEnter={onPressEnterHandler}
+            folderName={folderName}
+          />
+          {/* TODO: List Bookmark Folder */}
+        </>
+      )
+      }
       { isGuestUser
       { isGuestUser
         ? (
         ? (
           <h4 className="pl-3">
           <h4 className="pl-3">

+ 48 - 0
packages/app/src/components/Sidebar/Bookmarks/BookmarkFolder.tsx

@@ -0,0 +1,48 @@
+
+import React from 'react';
+
+import { useTranslation } from 'next-i18next';
+
+import ClosableTextInput from '~/components/Common/ClosableTextInput';
+
+
+type Props = {
+  onClickNewFolder: () => void
+  isRenameInputShown: boolean
+  folderName: string
+  onClickOutside: () => void
+  onPressEnter: (folderName: string) => void
+}
+const BookmarkFolder = (props: Props): JSX.Element => {
+  const {
+    onClickNewFolder, isRenameInputShown, folderName, onClickOutside, onPressEnter,
+  } = props;
+  const { t } = useTranslation();
+  return (
+    <>
+      <div className="col-8 mb-2 ">
+        <button
+          className="btn btn-block btn-outline-secondary rounded-pill d-flex justify-content-start align-middle"
+          onClick={onClickNewFolder}
+        >
+          <i className="fa fa fa-folder-o" style={{ fontSize: '1.4em' }}></i>
+          <span className="mx-2 ">New Folder</span>
+        </button>
+      </div>
+      {
+        isRenameInputShown && (
+          <div className="col-10 mb-2 ml-2 ">
+            <ClosableTextInput
+              value={folderName}
+              placeholder={t('Input Folder name')}
+              onClickOutside={onClickOutside}
+              onPressEnter={onPressEnter}
+            />
+          </div>
+        )
+      }
+    </>
+  );
+};
+
+export default BookmarkFolder;

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

@@ -3,6 +3,7 @@ import useSWRImmutable from 'swr/immutable';
 
 
 import { Nullable } from '~/interfaces/common';
 import { Nullable } from '~/interfaces/common';
 import { IPageHasId } from '~/interfaces/page';
 import { IPageHasId } from '~/interfaces/page';
+import { IBookmarkFolderDocument } from '~/server/models/bookmark-folder';
 
 
 import { apiv3Get } from '../client/util/apiv3-client';
 import { apiv3Get } from '../client/util/apiv3-client';
 import { IBookmarkInfo } from '../interfaces/bookmark-info';
 import { IBookmarkInfo } from '../interfaces/bookmark-info';
@@ -37,3 +38,12 @@ export const useSWRxCurrentUserBookmarks = (pageNum?: Nullable<number>): SWRResp
     }),
     }),
   );
   );
 };
 };
+
+export const useSWRxCurrentUserBookmarkFolders = () : SWRResponse<IBookmarkFolderDocument[], Error> => {
+  return useSWRImmutable(
+    '/bookmark-folder/list',
+    endpoint => apiv3Get(endpoint).then((response) => {
+      return response.data.bookmarkFolders;
+    }),
+  );
+};