Explorar o código

Merge branch 'imprv/gw7915-display-bookmark-count-on-bookmark-folder' of https://github.com/weseek/growi into imprv/gw7915-display-bookmark-count-on-bookmark-folder

jam411 %!s(int64=3) %!d(string=hai) anos
pai
achega
748d93f5c8

+ 17 - 28
packages/app/src/components/Bookmarks/BookmarkFolderItem.tsx

@@ -6,23 +6,22 @@ import { useTranslation } from 'next-i18next';
 import { useDrag, useDrop } from 'react-dnd';
 import { DropdownToggle } from 'reactstrap';
 
-import { apiv3Delete, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
+import { apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import CountBadge from '~/components/Common/CountBadge';
 import FolderIcon from '~/components/Icons/FolderIcon';
 import TriangleIcon from '~/components/Icons/TriangleIcon';
 import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 import { IPageHasId, IPageToDeleteWithMeta } from '~/interfaces/page';
-import { OnDeletedFunction } from '~/interfaces/ui';
+import { onDeletedBookmarkFolderFunction, OnDeletedFunction } from '~/interfaces/ui';
 import { useSWRBookmarkInfo } from '~/stores/bookmark';
 import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder';
-import { usePageDeleteModal } from '~/stores/modal';
+import { useBookmarkFolderDeleteModal, usePageDeleteModal } from '~/stores/modal';
 import { useSWRxCurrentPage } from '~/stores/page';
 
 import BookmarkFolderItemControl from './BookmarkFolderItemControl';
 import BookmarkFolderNameInput from './BookmarkFolderNameInput';
 import BookmarkItem from './BookmarkItem';
-import DeleteBookmarkFolderModal from './DeleteBookmarkFolderModal';
 
 
 type BookmarkFolderItemProps = {
@@ -47,10 +46,10 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
   const { mutate: mutateParentBookmarkFolder } = useSWRxBookamrkFolderAndChild(parent);
   const [isRenameAction, setIsRenameAction] = useState<boolean>(false);
   const [isCreateAction, setIsCreateAction] = useState<boolean>(false);
-  const [isDeleteFolderModalShown, setIsDeleteFolderModalShown] = useState<boolean>(false);
   const { data: currentPage } = useSWRxCurrentPage();
   const { mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id);
   const { open: openDeleteModal } = usePageDeleteModal();
+  const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal();
 
   useEffect(() => {
     if (childBookmarkFolderData != null) {
@@ -114,19 +113,6 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
 
   }, [mutateChildBookmarkData, t, targetFolder]);
 
-  // Delete Fodler handler
-  const onClickDeleteButtonHandler = useCallback(async() => {
-    try {
-      await apiv3Delete(`/bookmark-folder/${folderId}`);
-      setIsDeleteFolderModalShown(false);
-      loadParent();
-      mutateBookmarkInfo();
-      toastSuccess(t('toaster.delete_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }, [folderId, loadParent, mutateBookmarkInfo, t]);
 
   const onClickPlusButton = useCallback(async(e) => {
     e.stopPropagation();
@@ -262,12 +248,20 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
   }, []);
 
   const onClickDeleteHandler = useCallback(() => {
-    setIsDeleteFolderModalShown(true);
-  }, []);
+    const bookmarkFolderDeleteHandler: onDeletedBookmarkFolderFunction = (folderId) => {
+      if (typeof folderId !== 'string') {
+        return;
+      }
+      loadParent();
+      mutateBookmarkInfo();
+      toastSuccess(t('toaster.delete_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
+    };
 
-  const onDeleteFolderModalClose = useCallback(() => {
-    setIsDeleteFolderModalShown(false);
-  }, []);
+    if (bookmarkFolder == null) {
+      return;
+    }
+    openDeleteBookmarkFolderModal(bookmarkFolder, { onDeleted: bookmarkFolderDeleteHandler });
+  }, [bookmarkFolder, loadParent, mutateBookmarkInfo, openDeleteBookmarkFolderModal, t]);
 
 
   return (
@@ -350,11 +344,6 @@ const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkFolderIt
       {
         renderBookmarkItem()
       }
-      <DeleteBookmarkFolderModal
-        bookmarkFolder={bookmarkFolder}
-        isOpen={isDeleteFolderModalShown}
-        onClickDeleteButton={onClickDeleteButtonHandler}
-        onModalClose={onDeleteFolderModalClose} />
     </div>
   );
 };

+ 17 - 11
packages/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx

@@ -6,11 +6,13 @@ import {
   DropdownMenu, DropdownToggle, UncontrolledDropdown, UncontrolledTooltip,
 } from 'reactstrap';
 
-import { apiv3Delete, apiv3Post } from '~/client/util/apiv3-client';
+import { apiv3Post } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
+import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui';
 import { useSWRBookmarkInfo } from '~/stores/bookmark';
 import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder';
+import { useBookmarkFolderDeleteModal } from '~/stores/modal';
 import { useSWRxCurrentPage } from '~/stores/page';
 
 import FolderIcon from '../Icons/FolderIcon';
@@ -37,6 +39,7 @@ const BookmarkFolderMenuItem = (props: Props): JSX.Element => {
   const [isCreateAction, setIsCreateAction] = useState<boolean>(false);
   const { data: currentPage } = useSWRxCurrentPage();
   const { mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id);
+  const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal();
 
   const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => {
     try {
@@ -82,19 +85,22 @@ const BookmarkFolderMenuItem = (props: Props): JSX.Element => {
   }, []);
 
   // Delete folder handler
-  const onClickDeleteHandler = useCallback(async(e, item) => {
+  const onClickDeleteHandler = useCallback(async(e) => {
     e.stopPropagation();
-    try {
-      await apiv3Delete(`/bookmark-folder/${item._id}`);
-      mutateParentFolders();
+    const bookmarkFolderDeleteHandler: onDeletedBookmarkFolderFunction = (folderId) => {
+      if (typeof folderId !== 'string') {
+        return;
+      }
       mutateBookmarkInfo();
-      setIsOpen(false);
+      mutateParentFolders();
       toastSuccess(t('toaster.delete_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
+    };
+
+    if (item == null) {
+      return;
     }
-    catch (err) {
-      toastError(err);
-    }
-  }, [mutateBookmarkInfo, mutateParentFolders, t]);
+    openDeleteBookmarkFolderModal(item, { onDeleted: bookmarkFolderDeleteHandler });
+  }, [item, mutateBookmarkInfo, mutateParentFolders, openDeleteBookmarkFolderModal, t]);
 
   const onClickChildMenuItemHandler = useCallback(async(e, item) => {
     e.stopPropagation();
@@ -181,7 +187,7 @@ const BookmarkFolderMenuItem = (props: Props): JSX.Element => {
           id={`bookmark-delete-button-${item._id}`}
           className="text-danger ml-auto"
           color="transparent"
-          onClick={e => onClickDeleteHandler(e, item)}
+          onClick={e => onClickDeleteHandler(e)}
         >
           <i className="icon-fw icon-trash grw-page-control-dropdown-icon"></i>
         </DropdownToggle>

+ 0 - 53
packages/app/src/components/Bookmarks/DeleteBookmarkFolderModal.tsx

@@ -1,53 +0,0 @@
-
-import React from 'react';
-
-import { useTranslation } from 'next-i18next';
-import {
-  Modal, ModalBody, ModalFooter, ModalHeader,
-} from 'reactstrap';
-
-import FolderIcon from '~/components/Icons/FolderIcon';
-import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
-
-type DeleteBookmarkFolderModalProps = {
-  isOpen: boolean
-  bookmarkFolder: BookmarkFolderItems
-  onClickDeleteButton: () => void
-  onModalClose: () => void
-}
-
-const DeleteBookmarkFolderModal = (props: DeleteBookmarkFolderModalProps): JSX.Element => {
-  const { t } = useTranslation();
-  const {
-    isOpen, onClickDeleteButton, bookmarkFolder, onModalClose,
-  } = props;
-
-  return (
-    <Modal size="md" isOpen={isOpen} toggle={onModalClose} data-testid="page-delete-modal" className="grw-create-page">
-      <ModalHeader tag="h4" toggle={onModalClose} className="bg-danger text-light">
-        <i className="icon-fw icon-trash"></i>
-        {t('bookmark_folder.delete_modal.modal_header_label')}
-      </ModalHeader>
-      <ModalBody>
-        <div className="form-group pb-1">
-          <label>{ t('bookmark_folder.delete_modal.modal_body_description') }:</label><br />
-          <FolderIcon isOpen={false}/> {bookmarkFolder.name}
-        </div>
-        {t('bookmark_folder.delete_modal.modal_body_alert')}
-      </ModalBody>
-      <ModalFooter>
-        <button
-          type="button"
-          className="btn btn-danger"
-          onClick={onClickDeleteButton}
-        >
-          <i className="mr-1 icon-trash" aria-hidden="true"></i>
-          {t('bookmark_folder.delete_modal.modal_footer_button')}
-        </button>
-      </ModalFooter>
-    </Modal>
-
-  );
-};
-
-export default DeleteBookmarkFolderModal;

+ 70 - 0
packages/app/src/components/DeleteBookmarkFolderModal.tsx

@@ -0,0 +1,70 @@
+
+import React, { FC } from 'react';
+
+import { useTranslation } from 'next-i18next';
+import {
+  Modal, ModalBody, ModalFooter, ModalHeader,
+} from 'reactstrap';
+
+import { apiv3Delete } from '~/client/util/apiv3-client';
+import { toastError } from '~/client/util/toastr';
+import FolderIcon from '~/components/Icons/FolderIcon';
+import { useBookmarkFolderDeleteModal } from '~/stores/modal';
+
+
+const DeleteBookmarkFolderModal: FC = () => {
+  const { t } = useTranslation();
+  const { data: deleteBookmarkFolderModalData, close: closeBookmarkFolderDeleteModal } = useBookmarkFolderDeleteModal();
+  const isOpened = deleteBookmarkFolderModalData?.isOpened ?? false;
+
+  async function deleteBookmark() {
+    if (deleteBookmarkFolderModalData == null || deleteBookmarkFolderModalData.bookmarkFolder == null) {
+      return;
+    }
+    if (deleteBookmarkFolderModalData.bookmarkFolder != null) {
+      try {
+        await apiv3Delete(`/bookmark-folder/${deleteBookmarkFolderModalData.bookmarkFolder._id}`);
+        const onDeleted = deleteBookmarkFolderModalData.opts?.onDeleted;
+        if (onDeleted != null) {
+          onDeleted(deleteBookmarkFolderModalData.bookmarkFolder._id);
+        }
+        closeBookmarkFolderDeleteModal();
+      }
+      catch (err) {
+        toastError(err);
+      }
+    }
+  }
+  async function onClickDeleteButton() {
+    await deleteBookmark();
+  }
+
+  return (
+    <Modal size="md" isOpen={isOpened} toggle={closeBookmarkFolderDeleteModal} data-testid="page-delete-modal" className="grw-create-page">
+      <ModalHeader tag="h4" toggle={closeBookmarkFolderDeleteModal} className="bg-danger text-light">
+        <i className="icon-fw icon-trash"></i>
+        {t('bookmark_folder.delete_modal.modal_header_label')}
+      </ModalHeader>
+      <ModalBody>
+        <div className="form-group pb-1">
+          <label>{ t('bookmark_folder.delete_modal.modal_body_description') }:</label><br />
+          <FolderIcon isOpen={false}/> {deleteBookmarkFolderModalData?.bookmarkFolder?.name}
+        </div>
+        {t('bookmark_folder.delete_modal.modal_body_alert')}
+      </ModalBody>
+      <ModalFooter>
+        <button
+          type="button"
+          className="btn btn-danger"
+          onClick={onClickDeleteButton}
+        >
+          <i className="mr-1 icon-trash" aria-hidden="true"></i>
+          {t('bookmark_folder.delete_modal.modal_footer_button')}
+        </button>
+      </ModalFooter>
+    </Modal>
+
+  );
+};
+
+export default DeleteBookmarkFolderModal;

+ 2 - 0
packages/app/src/components/Layout/BasicLayout.tsx

@@ -22,6 +22,7 @@ const PageDeleteModal = dynamic(() => import('../PageDeleteModal'), { ssr: false
 const PageRenameModal = dynamic(() => import('../PageRenameModal'), { ssr: false });
 const PagePresentationModal = dynamic(() => import('../PagePresentationModal'), { ssr: false });
 const PageAccessoriesModal = dynamic(() => import('../PageAccessoriesModal'), { ssr: false });
+const DeleteBookmarkFolderModal = dynamic(() => import('../DeleteBookmarkFolderModal'), { ssr: false });
 // Fab
 const Fab = dynamic(() => import('../Fab').then(mod => mod.Fab), { ssr: false });
 
@@ -55,6 +56,7 @@ export const BasicLayout = ({ children, className }: Props): JSX.Element => {
         <PageDeleteModal />
         <PageRenameModal />
         <PageAccessoriesModal />
+        <DeleteBookmarkFolderModal />
       </DndProvider>
 
       <PagePresentationModal />

+ 1 - 0
packages/app/src/interfaces/ui.ts

@@ -26,3 +26,4 @@ export type OnDeletedFunction = (idOrPaths: string | string[], isRecursively: Nu
 export type OnRenamedFunction = (path: string) => void;
 export type OnDuplicatedFunction = (fromPath: string, toPath: string) => void;
 export type OnPutBackedFunction = (path: string) => void;
+export type onDeletedBookmarkFolderFunction = (bookmarkFolderId: string) => void;

+ 42 - 1
packages/app/src/stores/modal.tsx

@@ -3,9 +3,10 @@ import { useCallback, useMemo } from 'react';
 import { SWRResponse } from 'swr';
 
 import MarkdownTable from '~/client/models/MarkdownTable';
+import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 import { IPageToDeleteWithMeta, IPageToRenameWithMeta } from '~/interfaces/page';
 import {
-  OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction, OnPutBackedFunction,
+  OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction, OnPutBackedFunction, onDeletedBookmarkFolderFunction,
 } from '~/interfaces/ui';
 import { IUserGroupHasId } from '~/interfaces/user';
 
@@ -580,3 +581,43 @@ export const useConflictDiffModal = (): SWRResponse<ConflictDiffModalStatus, Err
     },
   });
 };
+
+/*
+* BookmarkFolderDeleteModal
+*/
+export type IDeleteBookmarkFolderModalOption = {
+  onDeleted?: onDeletedBookmarkFolderFunction,
+}
+
+type DeleteBookmarkFolderModalStatus = {
+  isOpened: boolean,
+  bookmarkFolder?: BookmarkFolderItems,
+  opts?: IDeleteBookmarkFolderModalOption,
+}
+
+type DeleteModalBookmarkFolderStatusUtils = {
+  open(
+    bookmarkFolder?: BookmarkFolderItems,
+    opts?: IDeleteBookmarkFolderModalOption,
+  ): Promise<DeleteBookmarkFolderModalStatus | undefined>,
+  close(): Promise<DeleteBookmarkFolderModalStatus | undefined>,
+}
+
+export const useBookmarkFolderDeleteModal = (status?: DeleteBookmarkFolderModalStatus):
+ SWRResponse<DeleteBookmarkFolderModalStatus, Error> & DeleteModalBookmarkFolderStatusUtils => {
+  const initialData: DeleteBookmarkFolderModalStatus = {
+    isOpened: false,
+  };
+  const swrResponse = useStaticSWR<DeleteBookmarkFolderModalStatus, Error>('deleteBookmarkFolderModalStatus', status, { fallbackData: initialData });
+
+  return {
+    ...swrResponse,
+    open: (
+        bookmarkFolder?: BookmarkFolderItems,
+        opts?: IDeleteBookmarkFolderModalOption,
+    ) => swrResponse.mutate({
+      isOpened: true, bookmarkFolder, opts,
+    }),
+    close: () => swrResponse.mutate({ isOpened: false }),
+  };
+};