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

+ 34 - 0
packages/app/src/client/util/bookmark-utils.ts

@@ -1,6 +1,10 @@
+import { IRevision, Ref } from '@growi/core';
+
 import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 
+import { apiv3Delete, apiv3Post, apiv3Put } from './apiv3-client';
 
+// Check if bookmark folder item has children
 export const hasChildren = (item: BookmarkFolderItems | BookmarkFolderItems[]): boolean => {
   if (item === null) {
     return false;
@@ -10,3 +14,33 @@ export const hasChildren = (item: BookmarkFolderItems | BookmarkFolderItems[]):
   }
   return item.children && item.children.length > 0;
 };
+
+// Add new folder helper
+export const addNewFolder = async(name: string, parent: string | null): Promise<void> => {
+  await apiv3Post('/bookmark-folder', { name, parent });
+};
+
+// Put bookmark to a folder
+export const addBookmarkToFolder = async(pageId: string, folderId: string | null): Promise<void> => {
+  await apiv3Post('/bookmark-folder/add-boookmark-to-folder', { pageId, folderId });
+};
+
+// Delete bookmark folder
+export const deleteBookmarkFolder = async(bookmarkFolderId: string): Promise<void> => {
+  await apiv3Delete(`/bookmark-folder/${bookmarkFolderId}`);
+};
+
+// Rename page from bookmark item control
+export const renamePage = async(pageId: string, revisionId: Ref<IRevision>, newPagePath: string): Promise<void> => {
+  await apiv3Put('/pages/rename', { pageId, revisionId, newPagePath });
+};
+
+// Update bookmark by isBookmarked status
+export const toggleBookmark = async(pageId: string, status: boolean): Promise<void> => {
+  await apiv3Put('/bookmark-folder/update-bookmark', { pageId, status });
+};
+
+// Update Bookmark folder
+export const updateBookmarkFolder = async(bookmarkFolderId: string, name: string, parent: string | null): Promise<void> => {
+  await apiv3Put('/bookmark-folder', { bookmarkFolderId, name, parent });
+};

+ 0 - 15
packages/app/src/client/util/input-validator-utils.ts

@@ -1,15 +0,0 @@
-import i18n from 'i18next';
-
-import { AlertInfo, AlertType } from '~/components/Common/ClosableTextInput';
-
-// Validator for closeable text input
-export const inputValidator = (title: string | null): AlertInfo | null => {
-
-  if (title == null || title === '' || title.trim() === '') {
-    return {
-      type: AlertType.WARNING,
-      message: i18n.t('form_validation.title_required'),
-    };
-  }
-  return null;
-};

+ 7 - 6
packages/app/src/components/Bookmarks/BookmarkFolderItem.tsx

@@ -6,8 +6,9 @@ import { useTranslation } from 'next-i18next';
 import { useDrag, useDrop } from 'react-dnd';
 import { DropdownToggle } from 'reactstrap';
 
-import { apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
-import { hasChildren } from '~/client/util/bookmark-utils';
+import {
+  addBookmarkToFolder, addNewFolder, hasChildren, updateBookmarkFolder,
+} from '~/client/util/bookmark-utils';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { FolderIcon } from '~/components/Icons/FolderIcon';
 import { TriangleIcon } from '~/components/Icons/TriangleIcon';
@@ -69,7 +70,7 @@ export const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkF
   // Rename  for bookmark folder handler
   const onPressEnterHandlerForRename = useCallback(async(folderName: string) => {
     try {
-      await apiv3Put('/bookmark-folder', { bookmarkFolderId: folderId, name: folderName, parent });
+      await updateBookmarkFolder(folderId, folderName, parent);
       mutateBookmarkData();
       setIsRenameAction(false);
       toastSuccess(t('toaster.update_successed', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
@@ -82,7 +83,7 @@ export const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkF
   // Create new folder / subfolder handler
   const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => {
     try {
-      await apiv3Post('/bookmark-folder', { name: folderName, parent: targetFolder });
+      await addNewFolder(folderName, targetFolder);
       setIsOpen(true);
       setIsCreateAction(false);
       mutateBookmarkData();
@@ -147,7 +148,7 @@ export const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkF
   const itemDropHandler = async(item: DragItemDataType, dragItemType: string | symbol | null) => {
     if (dragItemType === DRAG_ITEM_TYPE.FOLDER) {
       try {
-        await apiv3Put('/bookmark-folder', { bookmarkFolderId: item.bookmarkFolder._id, name: item.bookmarkFolder.name, parent: bookmarkFolder._id });
+        await updateBookmarkFolder(item.bookmarkFolder._id, item.bookmarkFolder.name, bookmarkFolder._id);
         mutateBookmarkData();
         toastSuccess(t('toaster.update_successed', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
       }
@@ -157,7 +158,7 @@ export const BookmarkFolderItem: FC<BookmarkFolderItemProps> = (props: BookmarkF
     }
     else {
       try {
-        await apiv3Post('/bookmark-folder/add-boookmark-to-folder', { pageId: item._id, folderId: bookmarkFolder._id });
+        await addBookmarkToFolder(item._id, bookmarkFolder._id);
         mutateBookmarkData();
         await mutateUserBookmarks();
         toastSuccess(t('toaster.add_succeeded', { target: t('bookmark_folder.bookmark'), ns: 'commons' }));

+ 10 - 6
packages/app/src/components/Bookmarks/BookmarkFolderMenu.tsx

@@ -7,7 +7,7 @@ import {
   DropdownItem, DropdownMenu, UncontrolledDropdown,
 } from 'reactstrap';
 
-import { apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
+import { addBookmarkToFolder, addNewFolder, toggleBookmark } from '~/client/util/bookmark-utils';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark';
@@ -33,14 +33,16 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => {
   const { data: currentPage } = useSWRxCurrentPage();
   const { data: userBookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id);
   const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks();
-  const isBookmarked = userBookmarkInfo?.isBookmarked;
+  const isBookmarked = userBookmarkInfo?.isBookmarked ?? false;
   const [isOpen, setIsOpen] = useState(false);
 
 
   const toggleBookmarkHandler = useCallback(async() => {
 
     try {
-      await apiv3Put('/bookmark-folder/update-bookmark', { pageId: currentPage?._id, status: isBookmarked });
+      if (currentPage != null) {
+        await toggleBookmark(currentPage._id, isBookmarked);
+      }
     }
     catch (err) {
       toastError(err);
@@ -95,7 +97,7 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => {
 
   const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => {
     try {
-      await apiv3Post('/bookmark-folder', { name: folderName, parent: null });
+      await addNewFolder(folderName, null);
       await mutateBookmarkFolderData();
       setIsCreateAction(false);
       toastSuccess(t('toaster.create_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
@@ -111,7 +113,9 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => {
       if (isBookmarked) {
         await toggleBookmarkHandler();
       }
-      await apiv3Post('/bookmark-folder/add-boookmark-to-folder', { pageId: currentPage?._id, folderId: itemId });
+      if (currentPage != null) {
+        await addBookmarkToFolder(currentPage._id, itemId);
+      }
       const toaster = isBookmarked ? 'toaster.update_successed' : 'toaster.add_succeeded';
       toastSuccess(t(toaster, { target: t('bookmark_folder.bookmark'), ns: 'commons' }));
       mutateBookmarkInfo();
@@ -123,7 +127,7 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => {
 
     mutateBookmarkFolderData();
     setSelectedItem(itemId);
-  }, [currentPage?._id, isBookmarked, mutateBookmarkFolderData, mutateBookmarkInfo, mutateUserBookmarks, toggleBookmarkHandler, t]);
+  }, [mutateBookmarkFolderData, isBookmarked, currentPage, t, mutateBookmarkInfo, mutateUserBookmarks, toggleBookmarkHandler]);
 
 
   const renderBookmarkMenuItem = (child?: BookmarkFolderItems[]) => {

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

@@ -1,4 +1,6 @@
-import React, { useCallback, useEffect, useState } from 'react';
+import React, {
+  useCallback, useEffect, useState,
+} from 'react';
 
 import { useTranslation } from 'next-i18next';
 import {
@@ -6,8 +8,9 @@ import {
   DropdownMenu, DropdownToggle, UncontrolledDropdown, UncontrolledTooltip,
 } from 'reactstrap';
 
-import { apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
-import { hasChildren } from '~/client/util/bookmark-utils';
+import {
+  addBookmarkToFolder, addNewFolder, hasChildren, toggleBookmark,
+} from '~/client/util/bookmark-utils';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui';
@@ -21,7 +24,6 @@ import { TriangleIcon } from '../Icons/TriangleIcon';
 
 import { BookmarkFolderNameInput } from './BookmarkFolderNameInput';
 
-
 type Props = {
   item: BookmarkFolderItems
   onSelectedChild: () => void
@@ -41,13 +43,13 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => {
   const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal();
   const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks();
 
-  const isBookmarked = userBookmarkInfo?.isBookmarked;
+  const isBookmarked = userBookmarkInfo?.isBookmarked ?? false;
 
   const childrenExists = hasChildren(item);
 
   const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => {
     try {
-      await apiv3Post('/bookmark-folder', { name: folderName, parent: item._id });
+      await addNewFolder(folderName, item._id);
       await mutateBookamrkData();
       setIsCreateAction(false);
       toastSuccess(t('toaster.create_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
@@ -56,7 +58,7 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => {
       toastError(err);
     }
 
-  }, [item, mutateBookamrkData, t]);
+  }, [item._id, mutateBookamrkData, t]);
 
   useEffect(() => {
     if (isOpen) {
@@ -106,10 +108,12 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => {
     e.stopPropagation();
     onSelectedChild();
     try {
-      if (isBookmarked) {
-        await apiv3Put('/bookmark-folder/update-bookmark', { pageId: currentPage?._id, status: isBookmarked });
+      if (isBookmarked && currentPage != null) {
+        await toggleBookmark(currentPage._id, isBookmarked);
+      }
+      if (currentPage != null) {
+        await addBookmarkToFolder(currentPage._id, item._id);
       }
-      await apiv3Post('/bookmark-folder/add-boookmark-to-folder', { pageId: currentPage?._id, folderId: item._id });
       const toaster = isBookmarked ? 'toaster.update_successed' : 'toaster.add_succeeded';
       toastSuccess(t(toaster, { target: t('bookmark_folder.bookmark'), ns: 'commons' }));
       mutateUserBookmarks();
@@ -120,7 +124,7 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => {
     catch (err) {
       toastError(err);
     }
-  }, [onSelectedChild, isBookmarked, mutateBookamrkData, mutateBookmarkInfo, currentPage?._id, mutateUserBookmarks, t]);
+  }, [onSelectedChild, isBookmarked, currentPage, t, mutateUserBookmarks, mutateBookamrkData, mutateBookmarkInfo]);
 
   const renderBookmarkSubMenuItem = useCallback(() => {
     if (!isOpen) {

+ 1 - 2
packages/app/src/components/Bookmarks/BookmarkFolderNameInput.tsx

@@ -1,7 +1,6 @@
 import { useTranslation } from 'next-i18next';
 
-import { inputValidator } from '~/client/util/input-validator-utils';
-import ClosableTextInput from '~/components/Common/ClosableTextInput';
+import ClosableTextInput, { inputValidator } from '~/components/Common/ClosableTextInput';
 
 
 type Props = {

+ 3 - 3
packages/app/src/components/Bookmarks/BookmarkFolderTree.tsx

@@ -4,7 +4,7 @@ import { useCallback } from 'react';
 import { useTranslation } from 'next-i18next';
 import { useDrop } from 'react-dnd';
 
-import { apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
+import { addBookmarkToFolder, updateBookmarkFolder } from '~/client/util/bookmark-utils';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { BookmarkFolderItems, DragItemType, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info';
 import { IPageHasId, IPageToDeleteWithMeta } from '~/interfaces/page';
@@ -69,7 +69,7 @@ export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element
   const itemDropHandler = async(item: DragItemDataType, dragType: string | null | symbol) => {
     if (dragType === DRAG_ITEM_TYPE.FOLDER) {
       try {
-        await apiv3Put('/bookmark-folder', { bookmarkFolderId: item.bookmarkFolder._id, name: item.bookmarkFolder.name, parent: null });
+        await updateBookmarkFolder(item.bookmarkFolder._id, item.bookmarkFolder.name, null);
         await mutateBookamrkData();
         toastSuccess(t('toaster.update_successed', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));
       }
@@ -79,7 +79,7 @@ export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element
     }
     else {
       try {
-        await apiv3Post('/bookmark-folder/add-boookmark-to-folder', { pageId: item._id, folderId: null });
+        await addBookmarkToFolder(item._id, null);
         await mutateUserBookmarks();
         toastSuccess(t('toaster.add_succeeded', { target: t('bookmark_folder.bookmark'), ns: 'commons' }));
       }

+ 3 - 8
packages/app/src/components/Bookmarks/BookmarkItem.tsx

@@ -8,15 +8,14 @@ import { useTranslation } from 'react-i18next';
 import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 
 import { unbookmark } from '~/client/services/page-operation';
-import { apiv3Put } from '~/client/util/apiv3-client';
-import { inputValidator } from '~/client/util/input-validator-utils';
+import { renamePage } from '~/client/util/bookmark-utils';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { BookmarkFolderItems, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info';
 import { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '~/interfaces/page';
 import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder';
 import { useSWRxPageInfo } from '~/stores/page';
 
-import ClosableTextInput from '../Common/ClosableTextInput';
+import ClosableTextInput, { inputValidator } from '../Common/ClosableTextInput';
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
 import { PageListItemS } from '../PageList/PageListItemS';
 
@@ -65,11 +64,7 @@ export const BookmarkItem = (props: Props): JSX.Element => {
 
     try {
       setRenameInputShown(false);
-      await apiv3Put('/pages/rename', {
-        pageId: bookmarkedPage._id,
-        revisionId: bookmarkedPage.revision,
-        newPagePath,
-      });
+      await renamePage(bookmarkedPage._id, bookmarkedPage.revision, newPagePath);
       onRenamed();
       toastSuccess(t('renamed_pages', { path: bookmarkedPage.path }));
     }

+ 13 - 0
packages/app/src/components/Common/ClosableTextInput.tsx

@@ -36,6 +36,9 @@ const ClosableTextInput: FC<ClosableTextInputProps> = memo((props: ClosableTextI
   const createValidation = async(inputText: string) => {
     if (props.inputValidator != null) {
       const alertInfo = await props.inputValidator(inputText);
+      if (alertInfo && alertInfo.message != null) {
+        alertInfo.message = t(alertInfo?.message);
+      }
       setAlertInfo(alertInfo);
     }
   };
@@ -134,6 +137,16 @@ const ClosableTextInput: FC<ClosableTextInputProps> = memo((props: ClosableTextI
   );
 });
 
+export const inputValidator = (title: string | null): AlertInfo | null => {
+  if (title == null || title === '' || title.trim() === '') {
+    return {
+      type: AlertType.WARNING,
+      message: 'form_validation.title_required',
+    };
+  }
+  return null;
+};
+
 ClosableTextInput.displayName = 'ClosableTextInput';
 
 export default ClosableTextInput;

+ 2 - 2
packages/app/src/components/DeleteBookmarkFolderModal.tsx

@@ -6,7 +6,7 @@ import {
   Modal, ModalBody, ModalFooter, ModalHeader,
 } from 'reactstrap';
 
-import { apiv3Delete } from '~/client/util/apiv3-client';
+import { deleteBookmarkFolder } from '~/client/util/bookmark-utils';
 import { toastError } from '~/client/util/toastr';
 import { FolderIcon } from '~/components/Icons/FolderIcon';
 import { useBookmarkFolderDeleteModal } from '~/stores/modal';
@@ -23,7 +23,7 @@ const DeleteBookmarkFolderModal: FC = () => {
     }
     if (deleteBookmarkFolderModalData.bookmarkFolder != null) {
       try {
-        await apiv3Delete(`/bookmark-folder/${deleteBookmarkFolderModalData.bookmarkFolder._id}`);
+        await deleteBookmarkFolder(deleteBookmarkFolderModalData.bookmarkFolder._id);
         const onDeleted = deleteBookmarkFolderModalData.opts?.onDeleted;
         if (onDeleted != null) {
           onDeleted(deleteBookmarkFolderModalData.bookmarkFolder._id);

+ 1 - 2
packages/app/src/components/PageList/BookmarkList.tsx

@@ -11,11 +11,10 @@ import { 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 { inputValidator } from '~/client/util/input-validator-utils';
 import { IPageHasId } from '~/interfaces/page';
 import loggerFactory from '~/utils/logger';
 
-import ClosableTextInput from '../Common/ClosableTextInput';
+import ClosableTextInput, { inputValidator } from '../Common/ClosableTextInput';
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
 
 import { PageListItemS } from './PageListItemS';

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

@@ -13,7 +13,6 @@ import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 import { bookmark, unbookmark, resumeRenameOperation } from '~/client/services/page-operation';
 import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotification';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
-import { inputValidator } from '~/client/util/input-validator-utils';
 import { TriangleIcon } from '~/components/Icons/TriangleIcon';
 import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
 import {
@@ -26,7 +25,7 @@ import { usePageTreeDescCountMap } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 import { shouldRecoverPagePaths } from '~/utils/page-operation';
 
-import ClosableTextInput from '../../Common/ClosableTextInput';
+import ClosableTextInput, { inputValidator } from '../../Common/ClosableTextInput';
 import CountBadge from '../../Common/CountBadge';
 import { PageItemControl } from '../../Common/Dropdown/PageItemControl';
 

+ 3 - 2
packages/app/src/components/UsersHomePageFooter.tsx

@@ -1,4 +1,4 @@
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useMemo, useState } from 'react';
 
 import { useTranslation } from 'next-i18next';
 
@@ -15,6 +15,7 @@ import { BookmarkFolderTree } from './Bookmarks/BookmarkFolderTree';
 import { CompressIcon } from './Icons/CompressIcon';
 import { ExpandIcon } from './Icons/ExpandIcon';
 import { FolderPlusIcon } from './Icons/FolderPlusIcon';
+import { addNewFolder } from '~/client/util/bookmark-utils';
 
 
 export type UsersHomePageFooterProps = {
@@ -30,7 +31,7 @@ export const UsersHomePageFooter = (props: UsersHomePageFooterProps): JSX.Elemen
 
   const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => {
     try {
-      await apiv3Post('/bookmark-folder', { name: folderName, parent: null });
+      await addNewFolder(folderName, null);
       await mutateChildBookmarkData();
       setIsCreateAction(false);
       toastSuccess(t('toaster.create_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' }));