From a53421710a90db72afc79fb3cee53f7ef8e37fbb Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 20 Apr 2023 07:01:57 +0000 Subject: [PATCH 01/14] delete dropdown zone to root and fix bookmark spell --- .../Bookmarks/BookmarkFolderItem.tsx | 15 +++- .../Bookmarks/BookmarkFolderMenu.tsx | 4 +- .../Bookmarks/BookmarkFolderMenuItem.tsx | 16 ++-- .../Bookmarks/BookmarkFolderTree.tsx | 80 ++++++++++--------- .../src/components/Bookmarks/BookmarkItem.tsx | 8 +- .../Sidebar/Bookmarks/BookmarkContents.tsx | 4 +- .../src/components/UsersHomePageFooter.tsx | 6 +- apps/app/src/stores/bookmark-folder.ts | 2 +- 8 files changed, 74 insertions(+), 61 deletions(-) diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx index 060ad4e5b9f..3f11aac37f4 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx @@ -17,7 +17,7 @@ import { import { IPageToDeleteWithMeta } from '~/interfaces/page'; import { onDeletedBookmarkFolderFunction, OnDeletedFunction } from '~/interfaces/ui'; import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder'; +import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useBookmarkFolderDeleteModal, usePageDeleteModal } from '~/stores/modal'; import { useSWRxCurrentPage } from '~/stores/page'; @@ -49,7 +49,7 @@ export const BookmarkFolderItem: FC = (props: BookmarkF const [targetFolder, setTargetFolder] = useState(folderId); const [isOpen, setIsOpen] = useState(_isOpen); - const { mutate: mutateBookmarkData } = useSWRxBookamrkFolderAndChild(); + const { mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); const [isRenameAction, setIsRenameAction] = useState(false); const [isCreateAction, setIsCreateAction] = useState(false); @@ -225,6 +225,15 @@ export const BookmarkFolderItem: FC = (props: BookmarkF openDeleteBookmarkFolderModal(bookmarkFolder, { onDeleted: bookmarkFolderDeleteHandler }); }, [bookmarkFolder, mutateBookmarkData, mutateBookmarkInfo, openDeleteBookmarkFolderModal, t]); + const onClickMoveToRootHandler = useCallback(async() => { + try { + await updateBookmarkFolder(bookmarkFolder._id, bookmarkFolder.name, null); + await mutateBookmarkData(); + } + catch (err) { + toastError(err); + } + }, [bookmarkFolder._id, bookmarkFolder.name, mutateBookmarkData]); return (
@@ -277,6 +286,8 @@ export const BookmarkFolderItem: FC = (props: BookmarkF
e.stopPropagation()}> diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx index ddd33c4439f..e111e3d2814 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx @@ -11,7 +11,7 @@ import { addBookmarkToFolder, addNewFolder, toggleBookmark } from '~/client/util import { toastError, toastSuccess } from '~/client/util/toastr'; import { BookmarkFolderItems } from '~/interfaces/bookmark-info'; import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder'; +import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useSWRxCurrentPage } from '~/stores/page'; import { FolderIcon } from '../Icons/FolderIcon'; @@ -28,7 +28,7 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { const { t } = useTranslation(); const { children } = props; const [isCreateAction, setIsCreateAction] = useState(false); - const { data: bookmarkFolders, mutate: mutateBookmarkFolderData } = useSWRxBookamrkFolderAndChild(); + const { data: bookmarkFolders, mutate: mutateBookmarkFolderData } = useSWRxBookmarkFolderAndChild(); const [selectedItem, setSelectedItem] = useState(null); const { data: currentPage } = useSWRxCurrentPage(); const { data: userBookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx index 7289232dcfa..89ff820083a 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx @@ -15,7 +15,7 @@ import { toastError, toastSuccess } from '~/client/util/toastr'; import { BookmarkFolderItems } from '~/interfaces/bookmark-info'; import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui'; import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder'; +import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useBookmarkFolderDeleteModal } from '~/stores/modal'; import { useSWRxCurrentPage } from '~/stores/page'; @@ -35,7 +35,7 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { } = props; const { t } = useTranslation(); const [isOpen, setIsOpen] = useState(false); - const { mutate: mutateBookamrkData } = useSWRxBookamrkFolderAndChild(); + const { mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); const [selectedItem, setSelectedItem] = useState(null); const [isCreateAction, setIsCreateAction] = useState(false); const { data: currentPage } = useSWRxCurrentPage(); @@ -50,7 +50,7 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { try { await addNewFolder(folderName, item._id); - await mutateBookamrkData(); + await mutateBookmarkData(); setIsCreateAction(false); toastSuccess(t('toaster.create_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' })); } @@ -58,7 +58,7 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { toastError(err); } - }, [item._id, mutateBookamrkData, t]); + }, [item._id, mutateBookmarkData, t]); useEffect(() => { if (isOpen) { @@ -94,7 +94,7 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { return; } mutateBookmarkInfo(); - mutateBookamrkData(); + mutateBookmarkData(); toastSuccess(t('toaster.delete_succeeded', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' })); }; @@ -102,7 +102,7 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { return; } openDeleteBookmarkFolderModal(item, { onDeleted: bookmarkFolderDeleteHandler }); - }, [item, mutateBookamrkData, mutateBookmarkInfo, openDeleteBookmarkFolderModal, t]); + }, [item, mutateBookmarkData, mutateBookmarkInfo, openDeleteBookmarkFolderModal, t]); const onClickChildMenuItemHandler = useCallback(async(e, item) => { e.stopPropagation(); @@ -117,14 +117,14 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { const toaster = isBookmarked ? 'toaster.update_successed' : 'toaster.add_succeeded'; toastSuccess(t(toaster, { target: t('bookmark_folder.bookmark'), ns: 'commons' })); mutateUserBookmarks(); - mutateBookamrkData(); + mutateBookmarkData(); setSelectedItem(item._id); mutateBookmarkInfo(); } catch (err) { toastError(err); } - }, [onSelectedChild, isBookmarked, currentPage, t, mutateUserBookmarks, mutateBookamrkData, mutateBookmarkInfo]); + }, [onSelectedChild, isBookmarked, currentPage, t, mutateUserBookmarks, mutateBookmarkData, mutateBookmarkInfo]); const renderBookmarkSubMenuItem = useCallback(() => { if (!isOpen) { diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx index 634a4e776f9..735995c7f4f 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx @@ -9,7 +9,7 @@ import { BookmarkFolderItems, DragItemType, DRAG_ITEM_TYPE } from '~/interfaces/ import { IPageHasId, IPageToDeleteWithMeta } from '~/interfaces/page'; import { OnDeletedFunction } from '~/interfaces/ui'; import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder'; +import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { usePageDeleteModal } from '~/stores/modal'; import { useSWRxCurrentPage } from '~/stores/page'; @@ -35,7 +35,7 @@ export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element const { t } = useTranslation(); const { isUserHomePage } = props; const { data: currentPage } = useSWRxCurrentPage(); - const { data: bookmarkFolderData, mutate: mutateBookamrkData } = useSWRxBookamrkFolderAndChild(); + const { data: bookmarkFolderData, mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); const { mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); @@ -61,43 +61,44 @@ export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element } mutateUserBookmarks(); mutateBookmarkInfo(); - mutateBookamrkData(); + mutateBookmarkData(); }; openDeleteModal([pageToDelete], { onDeleted: pageDeletedHandler }); - }, [mutateBookmarkInfo, mutateBookamrkData, mutateUserBookmarks, openDeleteModal, t]); - - const itemDropHandler = async(item: DragItemDataType, dragType: string | null | symbol) => { - if (dragType === DRAG_ITEM_TYPE.FOLDER) { - try { - await updateBookmarkFolder(item.bookmarkFolder._id, item.bookmarkFolder.name, null); - await mutateBookamrkData(); - toastSuccess(t('toaster.update_successed', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' })); - } - catch (err) { - toastError(err); - } - } - else { - try { - await addBookmarkToFolder(item._id, null); - await mutateUserBookmarks(); - toastSuccess(t('toaster.add_succeeded', { target: t('bookmark_folder.bookmark'), ns: 'commons' })); - } - catch (err) { - toastError(err); - } - } - - }; - const isDroppable = (item: DragItemDataType, dragType: string | null | symbol) => { - if (dragType === DRAG_ITEM_TYPE.FOLDER) { - const isRootFolder = item.level === 0; - return !isRootFolder; - } - const isRootBookmark = item.parentFolder == null; - return !isRootBookmark; - - }; + }, [mutateBookmarkInfo, mutateBookmarkData, mutateUserBookmarks, openDeleteModal, t]); + + /* TODO: update in bookmarks folder v2. */ + // const itemDropHandler = async(item: DragItemDataType, dragType: string | null | symbol) => { + // if (dragType === DRAG_ITEM_TYPE.FOLDER) { + // try { + // await updateBookmarkFolder(item.bookmarkFolder._id, item.bookmarkFolder.name, null); + // await mutateBookmarkData(); + // toastSuccess(t('toaster.update_successed', { target: t('bookmark_folder.bookmark_folder'), ns: 'commons' })); + // } + // catch (err) { + // toastError(err); + // } + // } + // else { + // try { + // await addBookmarkToFolder(item._id, null); + // await mutateUserBookmarks(); + // toastSuccess(t('toaster.add_succeeded', { target: t('bookmark_folder.bookmark'), ns: 'commons' })); + // } + // catch (err) { + // toastError(err); + // } + // } + + // }; + // const isDroppable = (item: DragItemDataType, dragType: string | null | symbol) => { + // if (dragType === DRAG_ITEM_TYPE.FOLDER) { + // const isRootFolder = item.level === 0; + // return !isRootFolder; + // } + // const isRootBookmark = item.parentFolder == null; + // return !isRootBookmark; + + // }; return (
@@ -128,7 +129,8 @@ export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element
))} - {bookmarkFolderData != null && bookmarkFolderData.length > 0 && ( + {/* TODO: update in bookmarks folder v2. Also delete drop_item_here in translation.json, if don't need it. */} + {/* {bookmarkFolderData != null && bookmarkFolderData.length > 0 && (
- )} + )} */}
); }; diff --git a/apps/app/src/components/Bookmarks/BookmarkItem.tsx b/apps/app/src/components/Bookmarks/BookmarkItem.tsx index d0f43f7ff16..b86e2ba1bdd 100644 --- a/apps/app/src/components/Bookmarks/BookmarkItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkItem.tsx @@ -12,7 +12,7 @@ import { ValidationTarget } from '~/client/util/input-validator'; import { toastError, toastSuccess } from '~/client/util/toastr'; import { BookmarkFolderItems, DragItemDataType, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info'; import { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '~/interfaces/page'; -import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder'; +import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useSWRxPageInfo } from '~/stores/page'; import ClosableTextInput from '../Common/ClosableTextInput'; @@ -41,7 +41,7 @@ export const BookmarkItem = (props: Props): JSX.Element => { const dPagePath = new DevidedPagePath(bookmarkedPage.path, false, true); const { latter: pageTitle, former: formerPagePath } = dPagePath; const bookmarkItemId = `bookmark-item-${bookmarkedPage._id}`; - const { mutate: mutateBookamrkData } = useSWRxBookamrkFolderAndChild(); + const { mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); const { data: fetchedPageInfo } = useSWRxPageInfo(bookmarkedPage._id); const paddingLeft = BASE_BOOKMARK_PADDING + (BASE_FOLDER_PADDING * (level + 1)); @@ -51,8 +51,8 @@ export const BookmarkItem = (props: Props): JSX.Element => { }; useEffect(() => { - mutateBookamrkData(); - }, [mutateBookamrkData]); + mutateBookmarkData(); + }, [mutateBookmarkData]); const bookmarkMenuItemClickHandler = useCallback(async() => { await unbookmark(bookmarkedPage._id); diff --git a/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx b/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx index 9a26fe37abc..1830cbdff76 100644 --- a/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx +++ b/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx @@ -7,14 +7,14 @@ import { toastError, toastSuccess } from '~/client/util/toastr'; import { BookmarkFolderNameInput } from '~/components/Bookmarks/BookmarkFolderNameInput'; import { BookmarkFolderTree } from '~/components/Bookmarks/BookmarkFolderTree'; import { FolderPlusIcon } from '~/components/Icons/FolderPlusIcon'; -import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder'; +import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; export const BookmarkContents = (): JSX.Element => { const { t } = useTranslation(); const [isCreateAction, setIsCreateAction] = useState(false); - const { mutate: mutateChildBookmarkData } = useSWRxBookamrkFolderAndChild(); + const { mutate: mutateChildBookmarkData } = useSWRxBookmarkFolderAndChild(); const onClickNewBookmarkFolder = useCallback(() => { diff --git a/apps/app/src/components/UsersHomePageFooter.tsx b/apps/app/src/components/UsersHomePageFooter.tsx index 77d7a55f7b3..fea84bd4b76 100644 --- a/apps/app/src/components/UsersHomePageFooter.tsx +++ b/apps/app/src/components/UsersHomePageFooter.tsx @@ -4,18 +4,18 @@ import { useTranslation } from 'next-i18next'; import { apiv3Post } from '~/client/util/apiv3-client'; +import { addNewFolder } from '~/client/util/bookmark-utils'; import { toastError, toastSuccess } from '~/client/util/toastr'; import { RecentlyCreatedIcon } from '~/components/Icons/RecentlyCreatedIcon'; import { RecentCreated } from '~/components/RecentCreated/RecentCreated'; import styles from '~/components/UsersHomePageFooter.module.scss'; -import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder'; +import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { BookmarkFolderNameInput } from './Bookmarks/BookmarkFolderNameInput'; 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 = { @@ -27,7 +27,7 @@ export const UsersHomePageFooter = (props: UsersHomePageFooterProps): JSX.Elemen const { creatorId } = props; const [isCreateAction, setIsCreateAction] = useState(false); const [isExpanded, setIsExpanded] = useState(false); - const { mutate: mutateChildBookmarkData } = useSWRxBookamrkFolderAndChild(); + const { mutate: mutateChildBookmarkData } = useSWRxBookmarkFolderAndChild(); const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { try { diff --git a/apps/app/src/stores/bookmark-folder.ts b/apps/app/src/stores/bookmark-folder.ts index db34223aba5..09fc560bbb7 100644 --- a/apps/app/src/stores/bookmark-folder.ts +++ b/apps/app/src/stores/bookmark-folder.ts @@ -4,7 +4,7 @@ import useSWRImmutable from 'swr/immutable'; import { apiv3Get } from '~/client/util/apiv3-client'; import { BookmarkFolderItems } from '~/interfaces/bookmark-info'; -export const useSWRxBookamrkFolderAndChild = (): SWRResponse => { +export const useSWRxBookmarkFolderAndChild = (): SWRResponse => { return useSWRImmutable( '/bookmark-folder/list', From 139692e38685b51f8833b3c6a96133a3aadd5f74 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 20 Apr 2023 07:02:59 +0000 Subject: [PATCH 02/14] fix lint error --- apps/app/src/components/UsersHomePageFooter.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/app/src/components/UsersHomePageFooter.module.scss b/apps/app/src/components/UsersHomePageFooter.module.scss index 5ecd8b19984..b66fbd0817f 100644 --- a/apps/app/src/components/UsersHomePageFooter.module.scss +++ b/apps/app/src/components/UsersHomePageFooter.module.scss @@ -1,4 +1,4 @@ -@use '~/styles/molecules/page_list'; +@use '@growi/ui/src/styles/molecules/page_list'; @use '~/styles/variables' as var; $grw-sidebar-content-header-height: 58px; $grw-sidebar-content-footer-height: 50px; From ed0c2984c8a8380a422fe6bcef7a4889723fa511 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 20 Apr 2023 07:03:53 +0000 Subject: [PATCH 03/14] fix bookmark tree button is not display --- .../Bookmarks/BookmarkFolderTree.module.scss | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderTree.module.scss b/apps/app/src/components/Bookmarks/BookmarkFolderTree.module.scss index 6ce549eab4b..4948953b4c0 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderTree.module.scss +++ b/apps/app/src/components/Bookmarks/BookmarkFolderTree.module.scss @@ -31,17 +31,11 @@ $grw-bookmark-item-padding-left: 35px; .list-group-item { .grw-visible-on-hover { display: none; - - &:hover { - display: block; - } } - .grw-count-badge { - display: block; - - &:hover { - display: none; + &:hover { + .grw-visible-on-hover { + display: block; } } From 03e0b109737afae339fb491d88a895ae11925448 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 20 Apr 2023 07:05:46 +0000 Subject: [PATCH 04/14] create new dropdown item for folder --- .../static/locales/en_US/translation.json | 3 +- .../static/locales/ja_JP/translation.json | 3 +- .../static/locales/zh_CN/translation.json | 3 +- .../Bookmarks/BookmarkFolderItemControl.tsx | 33 ++++++++++++++----- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/app/public/static/locales/en_US/translation.json b/apps/app/public/static/locales/en_US/translation.json index a45fef9f789..2c920883e9b 100644 --- a/apps/app/public/static/locales/en_US/translation.json +++ b/apps/app/public/static/locales/en_US/translation.json @@ -790,7 +790,8 @@ "new_folder": "New Folder", "delete": "Delete Folder", "drop_item_here": "Drag and drop item here", - "cancel_bookmark": "Un-bookmark this page" + "cancel_bookmark": "Un-bookmark this page", + "Move to the root": "Move to the root" }, "v5_page_migration": { "page_tree_not_avaliable" : "Page tree feature is not available yet.", diff --git a/apps/app/public/static/locales/ja_JP/translation.json b/apps/app/public/static/locales/ja_JP/translation.json index b2dc787eba9..9bbd5e348b0 100644 --- a/apps/app/public/static/locales/ja_JP/translation.json +++ b/apps/app/public/static/locales/ja_JP/translation.json @@ -824,7 +824,8 @@ "new_folder": "新しいフォルダ", "delete": "フォルダを削除", "drop_item_here": "ルートに配置する", - "cancel_bookmark": "このページのブックマークを解除" + "cancel_bookmark": "このページのブックマークを解除", + "move_to_root": "ルートに配置する" }, "v5_page_migration": { "page_tree_not_avaliable" : "Page Tree 機能は現在使用できません。", diff --git a/apps/app/public/static/locales/zh_CN/translation.json b/apps/app/public/static/locales/zh_CN/translation.json index 4cf62d0f0e9..1d5c842b65a 100644 --- a/apps/app/public/static/locales/zh_CN/translation.json +++ b/apps/app/public/static/locales/zh_CN/translation.json @@ -794,7 +794,8 @@ "new_folder": "新建文件夹", "delete": "删除文件夹", "drop_item_here": "将项目拖放到此处", - "cancel_bookmark": "取消收藏此页面" + "cancel_bookmark": "取消收藏此页面", + "move_to_root": "移动到根部" }, "v5_page_migration": { "page_tree_not_avaliable": "Page Tree 功能不可用", diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx index 022e67513e5..4115c813e54 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx @@ -5,16 +5,22 @@ import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle, } from 'reactstrap'; - -type BookmarkFolderItemControlProps = { +export const BookmarkFolderItemControl: React.FC<{ children?: React.ReactNode + isMoveToRoot: boolean onClickRename: () => void onClickDelete: () => void -} -export const BookmarkFolderItemControl = (props: BookmarkFolderItemControlProps): JSX.Element => { + onClickMoveToRoot: () => void +}> = ({ + children, + isMoveToRoot, + onClickRename, + onClickDelete, + onClickMoveToRoot, +}): JSX.Element => { const { t } = useTranslation(); - const { children, onClickRename, onClickDelete } = props; const [isOpen, setIsOpen] = useState(false); + return ( setIsOpen(!isOpen)}> { children ?? ( @@ -23,18 +29,29 @@ export const BookmarkFolderItemControl = (props: BookmarkFolderItemControlProps) ) } {t('Rename')} + {isMoveToRoot && ( + + + {t('bookmark_folder.move_to_root')} + + )} + Date: Thu, 20 Apr 2023 07:06:19 +0000 Subject: [PATCH 05/14] create new dropdown item for page --- .../Common/Dropdown/PageItemControl.tsx | 36 ++++++++++++++++++- .../components/DescendantsPageListModal.tsx | 3 +- apps/app/src/components/IdenticalPathPage.tsx | 2 ++ .../src/components/Navbar/SubNavButtons.tsx | 1 + apps/app/src/components/NotFoundPage.tsx | 4 +-- .../app/src/components/PrivateLegacyPages.tsx | 9 ++++- apps/app/src/components/SearchPage.tsx | 2 ++ .../src/components/Sidebar/PageTree/Item.tsx | 3 +- apps/app/src/components/TrashPageList.tsx | 2 +- 9 files changed, 55 insertions(+), 7 deletions(-) diff --git a/apps/app/src/components/Common/Dropdown/PageItemControl.tsx b/apps/app/src/components/Common/Dropdown/PageItemControl.tsx index ded93d0b769..afa085af980 100644 --- a/apps/app/src/components/Common/Dropdown/PageItemControl.tsx +++ b/apps/app/src/components/Common/Dropdown/PageItemControl.tsx @@ -1,15 +1,20 @@ -import React, { useState, useCallback, useEffect } from 'react'; +import React, { + useState, useCallback, useEffect, useMemo, +} from 'react'; import { useTranslation } from 'next-i18next'; import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem, } from 'reactstrap'; +import { addBookmarkToFolder } from '~/client/util/bookmark-utils'; +import { toastError } from '~/client/util/toastr'; import { NotAvailableForGuest } from '~/components/NotAvailableForGuest'; import { IPageInfoAll, isIPageInfoForOperation, } from '~/interfaces/page'; import { IPageOperationProcessData } from '~/interfaces/page-operation'; +import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; import { useSWRxPageInfo } from '~/stores/page'; import loggerFactory from '~/utils/logger'; import { shouldRecoverPagePaths } from '~/utils/page-operation'; @@ -19,6 +24,7 @@ const logger = loggerFactory('growi:cli:PageItemControl'); export const MenuItemType = { BOOKMARK: 'bookmark', + BOOKMARKS_TREE_MOVE_TO_ROOT: 'bookmarks_tree_move_to_root', RENAME: 'rename', DUPLICATE: 'duplicate', DELETE: 'delete', @@ -60,6 +66,8 @@ type DropdownMenuProps = CommonProps & { const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.Element => { const { t } = useTranslation(''); + const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); + const { pageId, isLoading, pageInfo, isEnableActions, forceHideMenuItems, operationProcessData, onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDuplicateMenuItem, onClickDeleteMenuItem, @@ -77,6 +85,20 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E await onClickBookmarkMenuItem(pageId, !pageInfo.isBookmarked); }, [onClickBookmarkMenuItem, pageId, pageInfo]); + const isMoveToRoot = useMemo(() => { + return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); + }, [pageId, userBookmarks]); + + const moveToRootClickedHandler = useCallback(async() => { + try { + await addBookmarkToFolder(pageId, null); + await mutateUserBookmarks(); + } + catch (err) { + toastError(err); + } + }, [mutateUserBookmarks, pageId]); + // eslint-disable-next-line react-hooks/rules-of-hooks const renameItemClickedHandler = useCallback(async() => { if (onClickRenameMenuItem == null) { @@ -172,6 +194,18 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E ) } + {/* Bookmarks Tree Move to Root */} + { !forceHideMenuItems?.includes(MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT) && isEnableActions && isMoveToRoot && ( + + + {t('bookmark_folder.move_to_root')} + + ) } + {/* Move/Rename */} { !forceHideMenuItems?.includes(MenuItemType.RENAME) && isEnableActions && pageInfo.isMovable && ( { if (status == null || status.path == null || !status.isOpened) { return <>; } - return ; + return ; }, i18n: t('page_list'), isLinkEnabled: () => !isSharedUser, diff --git a/apps/app/src/components/IdenticalPathPage.tsx b/apps/app/src/components/IdenticalPathPage.tsx index 64b04ff064e..8a87f258637 100644 --- a/apps/app/src/components/IdenticalPathPage.tsx +++ b/apps/app/src/components/IdenticalPathPage.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'next-i18next'; import { useCurrentPathname } from '~/stores/context'; import { useSWRxPageInfoForList, useSWRxPagesByPath } from '~/stores/page-listing'; +import { MenuItemType } from './Common/Dropdown/PageItemControl'; import { PageListItemL } from './PageList/PageListItemL'; @@ -76,6 +77,7 @@ export const IdenticalPathPage = (): JSX.Element => { isSelected={false} isEnableActions showPageUpdatedTime + forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]} /> ); })} diff --git a/apps/app/src/components/Navbar/SubNavButtons.tsx b/apps/app/src/components/Navbar/SubNavButtons.tsx index 775edf1e350..309300b1cf9 100644 --- a/apps/app/src/components/Navbar/SubNavButtons.tsx +++ b/apps/app/src/components/Navbar/SubNavButtons.tsx @@ -207,6 +207,7 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element const forceHideMenuItemsWithBookmark = forceHideMenuItems ?? []; forceHideMenuItemsWithBookmark.push(MenuItemType.BOOKMARK); + forceHideMenuItemsWithBookmark.push(MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT); return (
diff --git a/apps/app/src/components/NotFoundPage.tsx b/apps/app/src/components/NotFoundPage.tsx index ebf4add0f50..5893ce2d9a9 100644 --- a/apps/app/src/components/NotFoundPage.tsx +++ b/apps/app/src/components/NotFoundPage.tsx @@ -2,13 +2,13 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'next-i18next'; +import { MenuItemType } from './Common/Dropdown/PageItemControl'; import CustomNavAndContents from './CustomNavigation/CustomNavAndContents'; import { DescendantsPageList } from './DescendantsPageList'; import PageListIcon from './Icons/PageListIcon'; import TimeLineIcon from './Icons/TimeLineIcon'; import { PageTimeline } from './PageTimeline'; - type NotFoundPageProps = { path: string, } @@ -22,7 +22,7 @@ const NotFoundPage = (props: NotFoundPageProps): JSX.Element => { return { pagelist: { Icon: PageListIcon, - Content: () => , + Content: () => , i18n: t('page_list'), }, timeLine: { diff --git a/apps/app/src/components/PrivateLegacyPages.tsx b/apps/app/src/components/PrivateLegacyPages.tsx index 4f6e97036f5..f97d44224d3 100644 --- a/apps/app/src/components/PrivateLegacyPages.tsx +++ b/apps/app/src/components/PrivateLegacyPages.tsx @@ -436,7 +436,14 @@ const PrivateLegacyPages = (): JSX.Element => { ref={searchPageBaseRef} pages={data?.data} onSelectedPagesByCheckboxesChanged={selectedPagesByCheckboxesChangedHandler} - forceHideMenuItems={[MenuItemType.BOOKMARK, MenuItemType.RENAME, MenuItemType.DUPLICATE, MenuItemType.REVERT, MenuItemType.PATH_RECOVERY]} + forceHideMenuItems={[ + MenuItemType.BOOKMARK, + MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT, + MenuItemType.RENAME, + MenuItemType.DUPLICATE, + MenuItemType.REVERT, + MenuItemType.PATH_RECOVERY, + ]} // Components searchControl={searchControl} searchResultListHead={searchResultListHead} diff --git a/apps/app/src/components/SearchPage.tsx b/apps/app/src/components/SearchPage.tsx index b0a8cfefffc..ef1acf03efd 100644 --- a/apps/app/src/components/SearchPage.tsx +++ b/apps/app/src/components/SearchPage.tsx @@ -12,6 +12,7 @@ import { IFormattedSearchResult } from '~/interfaces/search'; import { useIsSearchServiceReachable, useShowPageLimitationL } from '~/stores/context'; import { ISearchConditions, ISearchConfigurations, useSWRxSearch } from '~/stores/search'; +import { MenuItemType } from './Common/Dropdown/PageItemControl'; import { NotAvailableForGuest } from './NotAvailableForGuest'; import PaginationWrapper from './PaginationWrapper'; import { OperateAllControl } from './SearchPage/OperateAllControl'; @@ -264,6 +265,7 @@ export const SearchPage = (): JSX.Element => { searchControl={searchControl} searchResultListHead={searchResultListHead} searchPager={searchPager} + forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]} /> ); }; diff --git a/apps/app/src/components/Sidebar/PageTree/Item.tsx b/apps/app/src/components/Sidebar/PageTree/Item.tsx index f3e3f8dec64..8b5402962a4 100644 --- a/apps/app/src/components/Sidebar/PageTree/Item.tsx +++ b/apps/app/src/components/Sidebar/PageTree/Item.tsx @@ -30,7 +30,7 @@ import { shouldRecoverPagePaths } from '~/utils/page-operation'; import ClosableTextInput from '../../Common/ClosableTextInput'; import CountBadge from '../../Common/CountBadge'; -import { PageItemControl } from '../../Common/Dropdown/PageItemControl'; +import { MenuItemType, PageItemControl } from '../../Common/Dropdown/PageItemControl'; import { ItemNode } from './ItemNode'; @@ -494,6 +494,7 @@ const Item: FC = (props: ItemProps) => { isInstantRename // Todo: It is wanted to find a better way to pass operationProcessData to PageItemControl operationProcessData={page.processData} + forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]} > {/* pass the color property to reactstrap dropdownToggle props. https://6-4-0--reactstrap.netlify.app/components/dropdowns/ */} diff --git a/apps/app/src/components/TrashPageList.tsx b/apps/app/src/components/TrashPageList.tsx index 629cb19afc3..eb5dbe88bd1 100644 --- a/apps/app/src/components/TrashPageList.tsx +++ b/apps/app/src/components/TrashPageList.tsx @@ -72,7 +72,7 @@ const DescendantsPageListForTrash = (): JSX.Element => { ); }; From ac6df5a51e400318bc111c226ed22d6c038ec12b Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 20 Apr 2023 08:51:17 +0000 Subject: [PATCH 06/14] imprv dropdown position --- .../Bookmarks/BookmarkFolderItemControl.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx index 4115c813e54..8f70cea303b 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx @@ -8,15 +8,15 @@ import { export const BookmarkFolderItemControl: React.FC<{ children?: React.ReactNode isMoveToRoot: boolean + onClickMoveToRoot: () => void onClickRename: () => void onClickDelete: () => void - onClickMoveToRoot: () => void }> = ({ children, isMoveToRoot, + onClickMoveToRoot, onClickRename, onClickDelete, - onClickMoveToRoot, }): JSX.Element => { const { t } = useTranslation(); const [isOpen, setIsOpen] = useState(false); @@ -33,13 +33,6 @@ export const BookmarkFolderItemControl: React.FC<{ container="body" style={{ zIndex: 1055 }} /* make it larger than $zindex-modal of bootstrap */ > - - - {t('Rename')} - {isMoveToRoot && ( )} + + + {t('Rename')} + From 1b60e9347d8c9be3cf645e397285d3dffe284892 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 27 Apr 2023 02:49:58 +0000 Subject: [PATCH 07/14] use additionalMenuItemOnTopRenderer --- .../src/components/Bookmarks/BookmarkItem.tsx | 42 +++++++++++++++++-- .../Common/Dropdown/PageItemControl.tsx | 34 +-------------- .../components/DescendantsPageListModal.tsx | 3 +- apps/app/src/components/IdenticalPathPage.tsx | 2 - .../src/components/Navbar/SubNavButtons.tsx | 1 - apps/app/src/components/NotFoundPage.tsx | 3 +- .../src/components/PageList/BookmarkList.tsx | 37 +++++++++++++++- .../app/src/components/PrivateLegacyPages.tsx | 1 - apps/app/src/components/SearchPage.tsx | 2 - .../src/components/Sidebar/PageTree/Item.tsx | 1 - apps/app/src/components/TrashPageList.tsx | 2 +- 11 files changed, 78 insertions(+), 50 deletions(-) diff --git a/apps/app/src/components/Bookmarks/BookmarkItem.tsx b/apps/app/src/components/Bookmarks/BookmarkItem.tsx index c0992dc304a..f3c6a319b17 100644 --- a/apps/app/src/components/Bookmarks/BookmarkItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkItem.tsx @@ -1,17 +1,22 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { + useCallback, useEffect, useState, useMemo, +} from 'react'; import nodePath from 'path'; import { DevidedPagePath, pathUtils } from '@growi/core'; import { useTranslation } from 'react-i18next'; -import { UncontrolledTooltip, DropdownToggle } from 'reactstrap'; +import { + UncontrolledTooltip, DropdownItem, DropdownToggle, +} from 'reactstrap'; import { unbookmark } from '~/client/services/page-operation'; -import { renamePage } from '~/client/util/bookmark-utils'; +import { addBookmarkToFolder, renamePage } from '~/client/util/bookmark-utils'; import { ValidationTarget } from '~/client/util/input-validator'; import { toastError } from '~/client/util/toastr'; import { BookmarkFolderItems, DragItemDataType, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info'; import { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '~/interfaces/page'; +import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useSWRxPageInfo } from '~/stores/page'; @@ -54,6 +59,36 @@ export const BookmarkItem = (props: Props): JSX.Element => { mutateBookmarkData(); }, [mutateBookmarkData]); + const pageId = bookmarkedPage._id; + + const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); + const isMoveToRoot = useMemo(() => { + return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); + }, [pageId, userBookmarks]); + + const moveToRootClickedHandler = useCallback(async() => { + try { + await addBookmarkToFolder(pageId, null); + await mutateUserBookmarks(); + } + catch (err) { + toastError(err); + } + }, [mutateUserBookmarks, pageId]); + + const additionalMenuItemOnTopRenderer = useMemo(() => { + return ( + + + {t('bookmark_folder.move_to_root')} + + ); + }, [moveToRootClickedHandler, t]); + const bookmarkMenuItemClickHandler = useCallback(async() => { await unbookmark(bookmarkedPage._id); onUnbookmarked(); @@ -130,6 +165,7 @@ export const BookmarkItem = (props: Props): JSX.Element => { onClickBookmarkMenuItem={bookmarkMenuItemClickHandler} onClickRenameMenuItem={renameMenuItemClickHandler} onClickDeleteMenuItem={deleteMenuItemClickHandler} + additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => additionalMenuItemOnTopRenderer) : undefined} > diff --git a/apps/app/src/components/Common/Dropdown/PageItemControl.tsx b/apps/app/src/components/Common/Dropdown/PageItemControl.tsx index afa085af980..4e6d74fff6a 100644 --- a/apps/app/src/components/Common/Dropdown/PageItemControl.tsx +++ b/apps/app/src/components/Common/Dropdown/PageItemControl.tsx @@ -1,5 +1,5 @@ import React, { - useState, useCallback, useEffect, useMemo, + useState, useCallback, useEffect, } from 'react'; import { useTranslation } from 'next-i18next'; @@ -7,14 +7,11 @@ import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem, } from 'reactstrap'; -import { addBookmarkToFolder } from '~/client/util/bookmark-utils'; -import { toastError } from '~/client/util/toastr'; import { NotAvailableForGuest } from '~/components/NotAvailableForGuest'; import { IPageInfoAll, isIPageInfoForOperation, } from '~/interfaces/page'; import { IPageOperationProcessData } from '~/interfaces/page-operation'; -import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; import { useSWRxPageInfo } from '~/stores/page'; import loggerFactory from '~/utils/logger'; import { shouldRecoverPagePaths } from '~/utils/page-operation'; @@ -24,7 +21,6 @@ const logger = loggerFactory('growi:cli:PageItemControl'); export const MenuItemType = { BOOKMARK: 'bookmark', - BOOKMARKS_TREE_MOVE_TO_ROOT: 'bookmarks_tree_move_to_root', RENAME: 'rename', DUPLICATE: 'duplicate', DELETE: 'delete', @@ -66,8 +62,6 @@ type DropdownMenuProps = CommonProps & { const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.Element => { const { t } = useTranslation(''); - const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); - const { pageId, isLoading, pageInfo, isEnableActions, forceHideMenuItems, operationProcessData, onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDuplicateMenuItem, onClickDeleteMenuItem, @@ -85,20 +79,6 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E await onClickBookmarkMenuItem(pageId, !pageInfo.isBookmarked); }, [onClickBookmarkMenuItem, pageId, pageInfo]); - const isMoveToRoot = useMemo(() => { - return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); - }, [pageId, userBookmarks]); - - const moveToRootClickedHandler = useCallback(async() => { - try { - await addBookmarkToFolder(pageId, null); - await mutateUserBookmarks(); - } - catch (err) { - toastError(err); - } - }, [mutateUserBookmarks, pageId]); - // eslint-disable-next-line react-hooks/rules-of-hooks const renameItemClickedHandler = useCallback(async() => { if (onClickRenameMenuItem == null) { @@ -194,18 +174,6 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E ) } - {/* Bookmarks Tree Move to Root */} - { !forceHideMenuItems?.includes(MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT) && isEnableActions && isMoveToRoot && ( - - - {t('bookmark_folder.move_to_root')} - - ) } - {/* Move/Rename */} { !forceHideMenuItems?.includes(MenuItemType.RENAME) && isEnableActions && pageInfo.isMovable && ( { if (status == null || status.path == null || !status.isOpened) { return <>; } - return ; + return ; }, i18n: t('page_list'), isLinkEnabled: () => !isSharedUser, diff --git a/apps/app/src/components/IdenticalPathPage.tsx b/apps/app/src/components/IdenticalPathPage.tsx index 8a87f258637..64b04ff064e 100644 --- a/apps/app/src/components/IdenticalPathPage.tsx +++ b/apps/app/src/components/IdenticalPathPage.tsx @@ -6,7 +6,6 @@ import { useTranslation } from 'next-i18next'; import { useCurrentPathname } from '~/stores/context'; import { useSWRxPageInfoForList, useSWRxPagesByPath } from '~/stores/page-listing'; -import { MenuItemType } from './Common/Dropdown/PageItemControl'; import { PageListItemL } from './PageList/PageListItemL'; @@ -77,7 +76,6 @@ export const IdenticalPathPage = (): JSX.Element => { isSelected={false} isEnableActions showPageUpdatedTime - forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]} /> ); })} diff --git a/apps/app/src/components/Navbar/SubNavButtons.tsx b/apps/app/src/components/Navbar/SubNavButtons.tsx index 309300b1cf9..775edf1e350 100644 --- a/apps/app/src/components/Navbar/SubNavButtons.tsx +++ b/apps/app/src/components/Navbar/SubNavButtons.tsx @@ -207,7 +207,6 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element const forceHideMenuItemsWithBookmark = forceHideMenuItems ?? []; forceHideMenuItemsWithBookmark.push(MenuItemType.BOOKMARK); - forceHideMenuItemsWithBookmark.push(MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT); return (
diff --git a/apps/app/src/components/NotFoundPage.tsx b/apps/app/src/components/NotFoundPage.tsx index 5893ce2d9a9..c5f9710a3d3 100644 --- a/apps/app/src/components/NotFoundPage.tsx +++ b/apps/app/src/components/NotFoundPage.tsx @@ -2,7 +2,6 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'next-i18next'; -import { MenuItemType } from './Common/Dropdown/PageItemControl'; import CustomNavAndContents from './CustomNavigation/CustomNavAndContents'; import { DescendantsPageList } from './DescendantsPageList'; import PageListIcon from './Icons/PageListIcon'; @@ -22,7 +21,7 @@ const NotFoundPage = (props: NotFoundPageProps): JSX.Element => { return { pagelist: { Icon: PageListIcon, - Content: () => , + Content: () => , i18n: t('page_list'), }, timeLine: { diff --git a/apps/app/src/components/PageList/BookmarkList.tsx b/apps/app/src/components/PageList/BookmarkList.tsx index 98bc63809ee..c973383eeb1 100644 --- a/apps/app/src/components/PageList/BookmarkList.tsx +++ b/apps/app/src/components/PageList/BookmarkList.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, useMemo } from 'react'; import nodePath from 'path'; @@ -6,13 +6,15 @@ import { IPageInfoAll, IPageToDeleteWithMeta, pathUtils, } from '@growi/core'; import { useTranslation } from 'next-i18next'; -import { DropdownToggle } from 'reactstrap'; +import { DropdownItem, DropdownToggle } from 'reactstrap'; import { unbookmark } from '~/client/services/page-operation'; import { apiv3Put } from '~/client/util/apiv3-client'; +import { addBookmarkToFolder } from '~/client/util/bookmark-utils'; import { ValidationTarget } from '~/client/util/input-validator'; import { toastError, toastSuccess } from '~/client/util/toastr'; import { IPageHasId } from '~/interfaces/page'; +import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; import loggerFactory from '~/utils/logger'; import ClosableTextInput from '../Common/ClosableTextInput'; @@ -35,6 +37,36 @@ export const BookmarkList = (props:Props): JSX.Element => { const { t } = useTranslation(); const [isRenameInputShown, setIsRenameInputShown] = useState(false); + const pageId = page._id; + + const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); + const isMoveToRoot = useMemo(() => { + return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); + }, [pageId, userBookmarks]); + + const moveToRootClickedHandler = useCallback(async() => { + try { + await addBookmarkToFolder(pageId, null); + await mutateUserBookmarks(); + } + catch (err) { + toastError(err); + } + }, [mutateUserBookmarks, pageId]); + + const additionalMenuItemOnTopRenderer = useMemo(() => { + return ( + + + {t('bookmark_folder.move_to_root')} + + ); + }, [moveToRootClickedHandler, t]); + const bookmarkMenuItemClickHandler = useCallback(async() => { await unbookmark(page._id); onUnbookmarked(); @@ -103,6 +135,7 @@ export const BookmarkList = (props:Props): JSX.Element => { onClickBookmarkMenuItem={bookmarkMenuItemClickHandler} onClickRenameMenuItem={() => setIsRenameInputShown(true)} onClickDeleteMenuItem={deleteMenuItemClickHandler} + additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => additionalMenuItemOnTopRenderer) : undefined} > diff --git a/apps/app/src/components/PrivateLegacyPages.tsx b/apps/app/src/components/PrivateLegacyPages.tsx index f97d44224d3..e6570a726a1 100644 --- a/apps/app/src/components/PrivateLegacyPages.tsx +++ b/apps/app/src/components/PrivateLegacyPages.tsx @@ -438,7 +438,6 @@ const PrivateLegacyPages = (): JSX.Element => { onSelectedPagesByCheckboxesChanged={selectedPagesByCheckboxesChangedHandler} forceHideMenuItems={[ MenuItemType.BOOKMARK, - MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT, MenuItemType.RENAME, MenuItemType.DUPLICATE, MenuItemType.REVERT, diff --git a/apps/app/src/components/SearchPage.tsx b/apps/app/src/components/SearchPage.tsx index ef1acf03efd..b0a8cfefffc 100644 --- a/apps/app/src/components/SearchPage.tsx +++ b/apps/app/src/components/SearchPage.tsx @@ -12,7 +12,6 @@ import { IFormattedSearchResult } from '~/interfaces/search'; import { useIsSearchServiceReachable, useShowPageLimitationL } from '~/stores/context'; import { ISearchConditions, ISearchConfigurations, useSWRxSearch } from '~/stores/search'; -import { MenuItemType } from './Common/Dropdown/PageItemControl'; import { NotAvailableForGuest } from './NotAvailableForGuest'; import PaginationWrapper from './PaginationWrapper'; import { OperateAllControl } from './SearchPage/OperateAllControl'; @@ -265,7 +264,6 @@ export const SearchPage = (): JSX.Element => { searchControl={searchControl} searchResultListHead={searchResultListHead} searchPager={searchPager} - forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]} /> ); }; diff --git a/apps/app/src/components/Sidebar/PageTree/Item.tsx b/apps/app/src/components/Sidebar/PageTree/Item.tsx index 8b5402962a4..ec20bce1505 100644 --- a/apps/app/src/components/Sidebar/PageTree/Item.tsx +++ b/apps/app/src/components/Sidebar/PageTree/Item.tsx @@ -494,7 +494,6 @@ const Item: FC = (props: ItemProps) => { isInstantRename // Todo: It is wanted to find a better way to pass operationProcessData to PageItemControl operationProcessData={page.processData} - forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]} > {/* pass the color property to reactstrap dropdownToggle props. https://6-4-0--reactstrap.netlify.app/components/dropdowns/ */} diff --git a/apps/app/src/components/TrashPageList.tsx b/apps/app/src/components/TrashPageList.tsx index eb5dbe88bd1..629cb19afc3 100644 --- a/apps/app/src/components/TrashPageList.tsx +++ b/apps/app/src/components/TrashPageList.tsx @@ -72,7 +72,7 @@ const DescendantsPageListForTrash = (): JSX.Element => { ); }; From 9b2b9a74117b72e5a888bb693ce2aefa6971f27a Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 27 Apr 2023 07:01:16 +0000 Subject: [PATCH 08/14] refactor called swr --- .../Bookmarks/BookmarkFolderItem.tsx | 65 +++++++++------ .../Bookmarks/BookmarkFolderTree.tsx | 81 +++++++++---------- .../src/components/Bookmarks/BookmarkItem.tsx | 49 +++++------ .../Bookmarks/BookmarkMoveToRootBtn.tsx | 22 +++++ .../src/components/PageList/BookmarkList.tsx | 19 +---- .../Sidebar/Bookmarks/BookmarkContents.tsx | 43 ++++------ 6 files changed, 142 insertions(+), 137 deletions(-) create mode 100644 apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx index 525592ec98a..9b7c9151bfa 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx @@ -2,8 +2,10 @@ import { FC, useCallback, useState, } from 'react'; +import type { IPageHasId } from '@growi/core'; import { useTranslation } from 'next-i18next'; import { DropdownToggle } from 'reactstrap'; +import type { KeyedMutator } from 'swr'; import { addBookmarkToFolder, addNewFolder, hasChildren, updateBookmarkFolder, @@ -12,34 +14,37 @@ import { toastError, toastSuccess } from '~/client/util/toastr'; import { FolderIcon } from '~/components/Icons/FolderIcon'; import { TriangleIcon } from '~/components/Icons/TriangleIcon'; import { - BookmarkFolderItems, DragItemDataType, DragItemType, DRAG_ITEM_TYPE, + BookmarkFolderItems, DragItemDataType, DragItemType, DRAG_ITEM_TYPE, IBookmarkInfo, } from '~/interfaces/bookmark-info'; import { IPageToDeleteWithMeta } from '~/interfaces/page'; import { onDeletedBookmarkFolderFunction, OnDeletedFunction } from '~/interfaces/ui'; -import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useBookmarkFolderDeleteModal, usePageDeleteModal } from '~/stores/modal'; -import { useSWRxCurrentPage } from '~/stores/page'; import { BookmarkFolderItemControl } from './BookmarkFolderItemControl'; import { BookmarkFolderNameInput } from './BookmarkFolderNameInput'; import { BookmarkItem } from './BookmarkItem'; import { DragAndDropWrapper } from './DragAndDropWrapper'; - type BookmarkFolderItemProps = { bookmarkFolder: BookmarkFolderItems isOpen?: boolean level: number root: string isUserHomePage?: boolean + bookmarkFolders: BookmarkFolderItems[] + mutateBookmarkFolders: KeyedMutator + userBookmarks: IPageHasId[] + mutateUserBookmarks: KeyedMutator + bookmarkInfo: IBookmarkInfo + mutateBookmarkInfo: KeyedMutator } export const BookmarkFolderItem: FC = (props: BookmarkFolderItemProps) => { const BASE_FOLDER_PADDING = 15; const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK]; const { - bookmarkFolder, isOpen: _isOpen = false, level, root, isUserHomePage, + bookmarkFolder, isOpen: _isOpen = false, level, root, isUserHomePage, bookmarkFolders, + mutateBookmarkFolders, userBookmarks, mutateUserBookmarks, bookmarkInfo, mutateBookmarkInfo, } = props; const { t } = useTranslation(); @@ -49,12 +54,10 @@ export const BookmarkFolderItem: FC = (props: BookmarkF const [targetFolder, setTargetFolder] = useState(folderId); const [isOpen, setIsOpen] = useState(_isOpen); - const { mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); - const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); const [isRenameAction, setIsRenameAction] = useState(false); const [isCreateAction, setIsCreateAction] = useState(false); - const { data: currentPage } = useSWRxCurrentPage(); - const { mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); + + const { open: openDeleteModal } = usePageDeleteModal(); const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal(); @@ -71,13 +74,13 @@ export const BookmarkFolderItem: FC = (props: BookmarkF const onPressEnterHandlerForRename = useCallback(async(folderName: string) => { try { await updateBookmarkFolder(folderId, folderName, parent); - mutateBookmarkData(); + mutateBookmarkFolders(); setIsRenameAction(false); } catch (err) { toastError(err); } - }, [folderId, mutateBookmarkData, parent]); + }, [folderId, mutateBookmarkFolders, parent]); // Create new folder / subfolder handler const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { @@ -85,12 +88,12 @@ export const BookmarkFolderItem: FC = (props: BookmarkF await addNewFolder(folderName, targetFolder); setIsOpen(true); setIsCreateAction(false); - mutateBookmarkData(); + mutateBookmarkFolders(); } catch (err) { toastError(err); } - }, [mutateBookmarkData, targetFolder]); + }, [mutateBookmarkFolders, targetFolder]); const onClickPlusButton = useCallback(async(e) => { @@ -114,16 +117,16 @@ export const BookmarkFolderItem: FC = (props: BookmarkF else { toastSuccess(t('deleted_pages', { path })); } - mutateBookmarkData(); + mutateBookmarkFolders(); mutateBookmarkInfo(); }; openDeleteModal([pageToDelete], { onDeleted: pageDeletedHandler }); - }, [mutateBookmarkInfo, mutateBookmarkData, openDeleteModal, t]); + }, [mutateBookmarkInfo, mutateBookmarkFolders, openDeleteModal, t]); const onUnbookmarkHandler = useCallback(() => { - mutateBookmarkData(); + mutateBookmarkFolders(); mutateBookmarkInfo(); - }, [mutateBookmarkInfo, mutateBookmarkData]); + }, [mutateBookmarkInfo, mutateBookmarkFolders]); const itemDropHandler = async(item: DragItemDataType, dragItemType: string | symbol | null) => { @@ -131,7 +134,7 @@ export const BookmarkFolderItem: FC = (props: BookmarkF try { if (item.bookmarkFolder != null) { await updateBookmarkFolder(item.bookmarkFolder._id, item.bookmarkFolder.name, bookmarkFolder._id); - mutateBookmarkData(); + mutateBookmarkFolders(); } } catch (err) { @@ -142,7 +145,7 @@ export const BookmarkFolderItem: FC = (props: BookmarkF try { if (item != null) { await addBookmarkToFolder(item._id, bookmarkFolder._id); - mutateBookmarkData(); + mutateBookmarkFolders(); await mutateUserBookmarks(); } } @@ -184,7 +187,13 @@ export const BookmarkFolderItem: FC = (props: BookmarkF bookmarkFolder={childFolder} level={level + 1} root={root} - isUserHomePage ={isUserHomePage} + isUserHomePage={isUserHomePage} + bookmarkFolders={bookmarkFolders} + mutateBookmarkFolders={mutateBookmarkFolders} + userBookmarks={userBookmarks} + mutateUserBookmarks={mutateUserBookmarks} + bookmarkInfo={bookmarkInfo} + mutateBookmarkInfo={mutateBookmarkInfo} />
); @@ -198,10 +207,14 @@ export const BookmarkFolderItem: FC = (props: BookmarkF bookmarkedPage={bookmark.page} key={bookmark._id} onUnbookmarked={onUnbookmarkHandler} - onRenamed={mutateBookmarkData} + onRenamed={mutateBookmarkFolders} onClickDeleteMenuItem={onClickDeleteBookmarkHandler} parentFolder={bookmarkFolder} level={level + 1} + bookmarkFolders={bookmarkFolders} + mutateBookmarkFolders={mutateBookmarkFolders} + userBookmarks={userBookmarks} + mutateUserBookmarks={mutateUserBookmarks} /> ); }); @@ -217,24 +230,24 @@ export const BookmarkFolderItem: FC = (props: BookmarkF return; } mutateBookmarkInfo(); - mutateBookmarkData(); + mutateBookmarkFolders(); }; if (bookmarkFolder == null) { return; } openDeleteBookmarkFolderModal(bookmarkFolder, { onDeleted: bookmarkFolderDeleteHandler }); - }, [bookmarkFolder, mutateBookmarkData, mutateBookmarkInfo, openDeleteBookmarkFolderModal]); + }, [bookmarkFolder, mutateBookmarkFolders, mutateBookmarkInfo, openDeleteBookmarkFolderModal]); const onClickMoveToRootHandler = useCallback(async() => { try { await updateBookmarkFolder(bookmarkFolder._id, bookmarkFolder.name, null); - await mutateBookmarkData(); + await mutateBookmarkFolders(); } catch (err) { toastError(err); } - }, [bookmarkFolder._id, bookmarkFolder.name, mutateBookmarkData]); + }, [bookmarkFolder._id, bookmarkFolder.name, mutateBookmarkFolders]); return (
diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx index 735995c7f4f..a5ae5e27184 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx @@ -1,44 +1,36 @@ -import { useCallback } from 'react'; +import React, { useCallback } from 'react'; import { useTranslation } from 'next-i18next'; -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'; import { OnDeletedFunction } from '~/interfaces/ui'; -import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; +import { useSWRxCurrentUserBookmarks, useSWRBookmarkInfo } from '~/stores/bookmark'; import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { usePageDeleteModal } from '~/stores/modal'; import { useSWRxCurrentPage } from '~/stores/page'; import { BookmarkFolderItem } from './BookmarkFolderItem'; import { BookmarkItem } from './BookmarkItem'; -import { DragAndDropWrapper } from './DragAndDropWrapper'; import styles from './BookmarkFolderTree.module.scss'; +// type DragItemDataType = { +// bookmarkFolder: BookmarkFolderItems +// level: number +// parentFolder: BookmarkFolderItems | null +// } & IPageHasId -type BookmarkFolderTreeProps = { - isUserHomePage?: boolean -} - -type DragItemDataType = { - bookmarkFolder: BookmarkFolderItems - level: number - parentFolder: BookmarkFolderItems | null - } & IPageHasId - -export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element => { - const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK]; +export const BookmarkFolderTree: React.FC<{isUserHomePage?: boolean}> = ({ isUserHomePage }) => { + // const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK]; const { t } = useTranslation(); - const { isUserHomePage } = props; + const { data: currentPage } = useSWRxCurrentPage(); - const { data: bookmarkFolderData, mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); + const { data: bookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); + const { data: bookmarkFolders, mutate: mutateBookmarkFolders } = useSWRxBookmarkFolderAndChild(); const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); - const { mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); - const { open: openDeleteModal } = usePageDeleteModal(); const onUnbookmarkHandler = useCallback(() => { @@ -48,23 +40,16 @@ export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element const onClickDeleteBookmarkHandler = useCallback((pageToDelete: IPageToDeleteWithMeta) => { const pageDeletedHandler: OnDeletedFunction = (pathOrPathsToDelete, _isRecursively, isCompletely) => { - if (typeof pathOrPathsToDelete !== 'string') { - return; - } - const path = pathOrPathsToDelete; - - if (isCompletely) { - toastSuccess(t('deleted_pages_completely', { path })); - } - else { - toastSuccess(t('deleted_pages', { path })); - } + if (typeof pathOrPathsToDelete !== 'string') return; + + toastSuccess(isCompletely ? t('deleted_pages_completely', { pathOrPathsToDelete }) : t('deleted_pages', { pathOrPathsToDelete })); + mutateUserBookmarks(); mutateBookmarkInfo(); - mutateBookmarkData(); + mutateBookmarkFolders(); }; openDeleteModal([pageToDelete], { onDeleted: pageDeletedHandler }); - }, [mutateBookmarkInfo, mutateBookmarkData, mutateUserBookmarks, openDeleteModal, t]); + }, [openDeleteModal, mutateUserBookmarks, mutateBookmarkInfo, mutateBookmarkFolders, t]); /* TODO: update in bookmarks folder v2. */ // const itemDropHandler = async(item: DragItemDataType, dragType: string | null | symbol) => { @@ -100,31 +85,45 @@ export const BookmarkFolderTree = (props: BookmarkFolderTreeProps): JSX.Element // }; + if (bookmarkFolders == null || userBookmarks == null || currentPage == null || bookmarkInfo == null) { + return <>; + } + return (
    - {bookmarkFolderData?.map((item) => { + {bookmarkFolders?.map((bookmarkFolder) => { return ( ); })} - {userBookmarks?.map(page => ( -
    + {userBookmarks?.map(userBookmark => ( +
    ))} diff --git a/apps/app/src/components/Bookmarks/BookmarkItem.tsx b/apps/app/src/components/Bookmarks/BookmarkItem.tsx index f3c6a319b17..7fbdc89c07b 100644 --- a/apps/app/src/components/Bookmarks/BookmarkItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkItem.tsx @@ -6,9 +6,8 @@ import nodePath from 'path'; import { DevidedPagePath, pathUtils } from '@growi/core'; import { useTranslation } from 'react-i18next'; -import { - UncontrolledTooltip, DropdownItem, DropdownToggle, -} from 'reactstrap'; +import { UncontrolledTooltip, DropdownToggle } from 'reactstrap'; +import type { KeyedMutator } from 'swr'; import { unbookmark } from '~/client/services/page-operation'; import { addBookmarkToFolder, renamePage } from '~/client/util/bookmark-utils'; @@ -16,14 +15,13 @@ import { ValidationTarget } from '~/client/util/input-validator'; import { toastError } from '~/client/util/toastr'; import { BookmarkFolderItems, DragItemDataType, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info'; import { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '~/interfaces/page'; -import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useSWRxPageInfo } from '~/stores/page'; import ClosableTextInput from '../Common/ClosableTextInput'; import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl'; import { PageListItemS } from '../PageList/PageListItemS'; +import { BookmarkMoveToRootBtn } from './BookmarkMoveToRootBtn'; import { DragAndDropWrapper } from './DragAndDropWrapper'; type Props = { @@ -31,8 +29,12 @@ type Props = { onUnbookmarked: () => void, onRenamed: () => void, onClickDeleteMenuItem: (pageToDelete: IPageToDeleteWithMeta) => void, - parentFolder: BookmarkFolderItems | null - level: number + parentFolder: BookmarkFolderItems | null, + level: number, + bookmarkFolders: BookmarkFolderItems[], + mutateBookmarkFolders: KeyedMutator, + userBookmarks: IPageHasId[], + mutateUserBookmarks: KeyedMutator, } export const BookmarkItem = (props: Props): JSX.Element => { @@ -40,13 +42,16 @@ export const BookmarkItem = (props: Props): JSX.Element => { const BASE_BOOKMARK_PADDING = 20; const { t } = useTranslation(); const { - bookmarkedPage, onUnbookmarked, onRenamed, onClickDeleteMenuItem, parentFolder, level, + bookmarkedPage, + onUnbookmarked, + onRenamed, + onClickDeleteMenuItem, parentFolder, level, + mutateBookmarkFolders, userBookmarks, mutateUserBookmarks, } = props; const [isRenameInputShown, setRenameInputShown] = useState(false); const dPagePath = new DevidedPagePath(bookmarkedPage.path, false, true); const { latter: pageTitle, former: formerPagePath } = dPagePath; const bookmarkItemId = `bookmark-item-${bookmarkedPage._id}`; - const { mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); const { data: fetchedPageInfo } = useSWRxPageInfo(bookmarkedPage._id); const paddingLeft = BASE_BOOKMARK_PADDING + (BASE_FOLDER_PADDING * (level + 1)); @@ -56,16 +61,11 @@ export const BookmarkItem = (props: Props): JSX.Element => { }; useEffect(() => { - mutateBookmarkData(); - }, [mutateBookmarkData]); + mutateBookmarkFolders(); + }, [mutateBookmarkFolders]); const pageId = bookmarkedPage._id; - const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); - const isMoveToRoot = useMemo(() => { - return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); - }, [pageId, userBookmarks]); - const moveToRootClickedHandler = useCallback(async() => { try { await addBookmarkToFolder(pageId, null); @@ -76,18 +76,9 @@ export const BookmarkItem = (props: Props): JSX.Element => { } }, [mutateUserBookmarks, pageId]); - const additionalMenuItemOnTopRenderer = useMemo(() => { - return ( - - - {t('bookmark_folder.move_to_root')} - - ); - }, [moveToRootClickedHandler, t]); + const isMoveToRoot = useMemo(() => { + return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); + }, [pageId, userBookmarks]); const bookmarkMenuItemClickHandler = useCallback(async() => { await unbookmark(bookmarkedPage._id); @@ -165,7 +156,7 @@ export const BookmarkItem = (props: Props): JSX.Element => { onClickBookmarkMenuItem={bookmarkMenuItemClickHandler} onClickRenameMenuItem={renameMenuItemClickHandler} onClickDeleteMenuItem={deleteMenuItemClickHandler} - additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => additionalMenuItemOnTopRenderer) : undefined} + additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => ) : undefined} > diff --git a/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx b/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx new file mode 100644 index 00000000000..43e84740b82 --- /dev/null +++ b/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import { useTranslation } from 'react-i18next'; +import { DropdownItem } from 'reactstrap'; + +export const BookmarkMoveToRootBtn: React.FC<{ + moveToRootClickedHandler: () => Promise +}> = React.memo(({ moveToRootClickedHandler }) => { + const { t } = useTranslation(); + + return ( + + + {t('bookmark_folder.move_to_root')} + + ); +}); +BookmarkMoveToRootBtn.displayName = 'BookmarkMoveToRootBtn'; diff --git a/apps/app/src/components/PageList/BookmarkList.tsx b/apps/app/src/components/PageList/BookmarkList.tsx index c973383eeb1..04aa88ecd81 100644 --- a/apps/app/src/components/PageList/BookmarkList.tsx +++ b/apps/app/src/components/PageList/BookmarkList.tsx @@ -6,7 +6,7 @@ import { IPageInfoAll, IPageToDeleteWithMeta, pathUtils, } from '@growi/core'; import { useTranslation } from 'next-i18next'; -import { DropdownItem, DropdownToggle } from 'reactstrap'; +import { DropdownToggle } from 'reactstrap'; import { unbookmark } from '~/client/services/page-operation'; import { apiv3Put } from '~/client/util/apiv3-client'; @@ -17,6 +17,7 @@ import { IPageHasId } from '~/interfaces/page'; import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; import loggerFactory from '~/utils/logger'; +import { BookmarkMoveToRootBtn } from '../Bookmarks/BookmarkMoveToRootBtn'; import ClosableTextInput from '../Common/ClosableTextInput'; import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl'; @@ -40,6 +41,7 @@ export const BookmarkList = (props:Props): JSX.Element => { const pageId = page._id; const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); + const isMoveToRoot = useMemo(() => { return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); }, [pageId, userBookmarks]); @@ -54,19 +56,6 @@ export const BookmarkList = (props:Props): JSX.Element => { } }, [mutateUserBookmarks, pageId]); - const additionalMenuItemOnTopRenderer = useMemo(() => { - return ( - - - {t('bookmark_folder.move_to_root')} - - ); - }, [moveToRootClickedHandler, t]); - const bookmarkMenuItemClickHandler = useCallback(async() => { await unbookmark(page._id); onUnbookmarked(); @@ -135,7 +124,7 @@ export const BookmarkList = (props:Props): JSX.Element => { onClickBookmarkMenuItem={bookmarkMenuItemClickHandler} onClickRenameMenuItem={() => setIsRenameInputShown(true)} onClickDeleteMenuItem={deleteMenuItemClickHandler} - additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => additionalMenuItemOnTopRenderer) : undefined} + additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => ) : undefined} > diff --git a/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx b/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx index 7691c8ae61a..68b3bc6dff5 100644 --- a/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx +++ b/apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx @@ -3,37 +3,38 @@ import React, { useCallback, useState } from 'react'; import { useTranslation } from 'next-i18next'; import { apiv3Post } from '~/client/util/apiv3-client'; -import { toastError, toastSuccess } from '~/client/util/toastr'; +import { toastError } from '~/client/util/toastr'; import { BookmarkFolderNameInput } from '~/components/Bookmarks/BookmarkFolderNameInput'; import { BookmarkFolderTree } from '~/components/Bookmarks/BookmarkFolderTree'; import { FolderPlusIcon } from '~/components/Icons/FolderPlusIcon'; import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; - export const BookmarkContents = (): JSX.Element => { const { t } = useTranslation(); const [isCreateAction, setIsCreateAction] = useState(false); - const { mutate: mutateChildBookmarkData } = useSWRxBookmarkFolderAndChild(); - + const { mutate: mutateBookmarkFolders } = useSWRxBookmarkFolderAndChild(); const onClickNewBookmarkFolder = useCallback(() => { setIsCreateAction(true); }, []); - const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { + const onClickonClickOutsideHandler = useCallback(() => { + setIsCreateAction(false); + }, []); + const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { try { await apiv3Post('/bookmark-folder', { name: folderName, parent: null }); - await mutateChildBookmarkData(); + await mutateBookmarkFolders(); setIsCreateAction(false); } catch (err) { toastError(err); } - }, [mutateChildBookmarkData]); + }, [mutateBookmarkFolders]); - const renderAddNewBookmarkFolder = useCallback(() => ( + return ( <>
    - { - isCreateAction && ( -
    - setIsCreateAction(false)} - onPressEnter={onPressEnterHandlerForCreate} - /> -
    - ) - } - - ), [isCreateAction, onClickNewBookmarkFolder, onPressEnterHandlerForCreate, t]); - - return ( - <> - { - renderAddNewBookmarkFolder() - } + {isCreateAction && ( +
    + +
    + )} ); From 84b53823fd6fb514dee16f9961bc243b5cc7c11b Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 27 Apr 2023 07:20:49 +0000 Subject: [PATCH 09/14] refactor use swr position --- .../Bookmarks/BookmarkFolderMenu.tsx | 50 ++++++++++-------- .../Bookmarks/BookmarkFolderMenuItem.tsx | 52 +++++++++++-------- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx index 542f99abfa2..c8c81264c32 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx @@ -19,24 +19,20 @@ import { FolderIcon } from '../Icons/FolderIcon'; import { BookmarkFolderMenuItem } from './BookmarkFolderMenuItem'; import { BookmarkFolderNameInput } from './BookmarkFolderNameInput'; - -type Props = { - children?: React.ReactNode -} - -export const BookmarkFolderMenu = (props: Props): JSX.Element => { +export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ children }): JSX.Element => { const { t } = useTranslation(); - const { children } = props; + const [isCreateAction, setIsCreateAction] = useState(false); - const { data: bookmarkFolders, mutate: mutateBookmarkFolderData } = useSWRxBookmarkFolderAndChild(); const [selectedItem, setSelectedItem] = useState(null); + const [isOpen, setIsOpen] = useState(false); + + const { data: bookmarkFolders, mutate: mutateBookmarkFolders } = useSWRxBookmarkFolderAndChild(); const { data: currentPage } = useSWRxCurrentPage(); - const { data: userBookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); + const { data: bookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); const { mutate: mutatePageInfo } = useSWRxPageInfo(currentPage?._id); - const isBookmarked = userBookmarkInfo?.isBookmarked ?? false; - const [isOpen, setIsOpen] = useState(false); + const isBookmarked = bookmarkInfo?.isBookmarked ?? false; const toggleBookmarkHandler = useCallback(async() => { try { @@ -57,14 +53,14 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { await toggleBookmarkHandler(); mutateUserBookmarks(); mutateBookmarkInfo(); - mutateBookmarkFolderData(); + mutateBookmarkFolders(); mutatePageInfo(); setSelectedItem(null); - }, [mutateBookmarkFolderData, mutateBookmarkInfo, mutatePageInfo, mutateUserBookmarks, toggleBookmarkHandler]); + }, [mutateBookmarkFolders, mutateBookmarkInfo, mutatePageInfo, mutateUserBookmarks, toggleBookmarkHandler]); const toggleHandler = useCallback(async() => { setIsOpen(!isOpen); - mutateBookmarkFolderData(); + mutateBookmarkFolders(); if (isOpen && bookmarkFolders != null) { bookmarkFolders.forEach((bookmarkFolder) => { bookmarkFolder.bookmarks.forEach((bookmark) => { @@ -89,7 +85,7 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { }, [ isOpen, - mutateBookmarkFolderData, + mutateBookmarkFolders, bookmarkFolders, isBookmarked, currentPage?._id, @@ -99,7 +95,6 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { mutatePageInfo, ]); - const isBookmarkFolderExists = useCallback((): boolean => { return bookmarkFolders != null && bookmarkFolders.length > 0; }, [bookmarkFolders]); @@ -107,13 +102,13 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { try { await addNewFolder(folderName, null); - await mutateBookmarkFolderData(); + await mutateBookmarkFolders(); setIsCreateAction(false); } catch (err) { toastError(err); } - }, [mutateBookmarkFolderData]); + }, [mutateBookmarkFolders]); const onMenuItemClickHandler = useCallback(async(itemId: string) => { try { @@ -130,14 +125,13 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { toastError(err); } - mutateBookmarkFolderData(); + mutateBookmarkFolders(); setSelectedItem(itemId); - }, [mutateBookmarkFolderData, isBookmarked, currentPage, mutateBookmarkInfo, mutateUserBookmarks, toggleBookmarkHandler]); - + }, [mutateBookmarkFolders, isBookmarked, currentPage, mutateBookmarkInfo, mutateUserBookmarks, toggleBookmarkHandler]); const renderBookmarkMenuItem = (child?: BookmarkFolderItems[]) => { const renderSubmenu = () => { - if (child == null) { + if (child == null || currentPage == null || bookmarkInfo == null) { return <>; } return ( @@ -154,6 +148,11 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { item={folder} isSelected={selectedItem === folder._id} onSelectedChild={() => setSelectedItem(null)} + currentPage={currentPage} + bookmarkInfo={bookmarkInfo} + mutateBookmarkFolders={mutateBookmarkFolders} + mutateBookmarkInfo={mutateBookmarkInfo} + mutateUserBookmarks={mutateUserBookmarks} /> {isOpen && renderSubmenu()}
    @@ -199,7 +198,7 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { )} - {isBookmarkFolderExists() && ( + {isBookmarkFolderExists() && currentPage != null && bookmarkInfo != null && ( <> {bookmarkFolders?.map(folder => ( @@ -214,6 +213,11 @@ export const BookmarkFolderMenu = (props: Props): JSX.Element => { item={folder} isSelected={selectedItem === folder._id} onSelectedChild={() => setSelectedItem(null)} + currentPage={currentPage} + bookmarkInfo={bookmarkInfo} + mutateBookmarkFolders={mutateBookmarkFolders} + mutateBookmarkInfo={mutateBookmarkInfo} + mutateUserBookmarks={mutateUserBookmarks} /> {isOpen && renderSubmenu()}
diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx index 9e8664791f4..9f6b1c53136 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx @@ -2,61 +2,69 @@ import React, { useCallback, useEffect, useState, } from 'react'; +import type { IPagePopulatedToShowRevision } from '@growi/core'; import { useTranslation } from 'next-i18next'; import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown, } from 'reactstrap'; +import type { KeyedMutator } from 'swr'; import { addBookmarkToFolder, addNewFolder, hasChildren, toggleBookmark, } from '~/client/util/bookmark-utils'; import { toastError } from '~/client/util/toastr'; -import { BookmarkFolderItems } from '~/interfaces/bookmark-info'; +import { BookmarkFolderItems, IBookmarkInfo } from '~/interfaces/bookmark-info'; +import { IPageHasId } from '~/interfaces/page'; import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui'; -import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; import { useBookmarkFolderDeleteModal } from '~/stores/modal'; -import { useSWRxCurrentPage } from '~/stores/page'; import { FolderIcon } from '../Icons/FolderIcon'; import { TriangleIcon } from '../Icons/TriangleIcon'; import { BookmarkFolderNameInput } from './BookmarkFolderNameInput'; -type Props = { +export const BookmarkFolderMenuItem: React.FC<{ item: BookmarkFolderItems - onSelectedChild: () => void isSelected: boolean -} -export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { - const { - item, isSelected, onSelectedChild, - } = props; + onSelectedChild: () => void + currentPage: IPagePopulatedToShowRevision + bookmarkInfo: IBookmarkInfo + mutateBookmarkFolders: KeyedMutator + mutateBookmarkInfo: KeyedMutator + mutateUserBookmarks: KeyedMutator +}> = ({ + item, + isSelected, + onSelectedChild, + currentPage, + bookmarkInfo, + mutateBookmarkFolders, + mutateBookmarkInfo, + mutateUserBookmarks, +}) => { const { t } = useTranslation(); + const [isOpen, setIsOpen] = useState(false); - const { mutate: mutateBookmarkData } = useSWRxBookmarkFolderAndChild(); const [selectedItem, setSelectedItem] = useState(null); const [isCreateAction, setIsCreateAction] = useState(false); - const { data: currentPage } = useSWRxCurrentPage(); - const { data: userBookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); + const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal(); - const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); - const isBookmarked = userBookmarkInfo?.isBookmarked ?? false; + const isBookmarked = bookmarkInfo?.isBookmarked ?? false; const childrenExists = hasChildren(item); const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { try { await addNewFolder(folderName, item._id); - await mutateBookmarkData(); + await mutateBookmarkFolders(); setIsCreateAction(false); } catch (err) { toastError(err); } - }, [item._id, mutateBookmarkData]); + }, [item._id, mutateBookmarkFolders]); useEffect(() => { if (isOpen) { @@ -92,14 +100,14 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { return; } mutateBookmarkInfo(); - mutateBookmarkData(); + mutateBookmarkFolders(); }; if (item == null) { return; } openDeleteBookmarkFolderModal(item, { onDeleted: bookmarkFolderDeleteHandler }); - }, [item, mutateBookmarkData, mutateBookmarkInfo, openDeleteBookmarkFolderModal]); + }, [item, mutateBookmarkFolders, mutateBookmarkInfo, openDeleteBookmarkFolderModal]); const onClickChildMenuItemHandler = useCallback(async(e, item) => { e.stopPropagation(); @@ -112,14 +120,14 @@ export const BookmarkFolderMenuItem = (props: Props): JSX.Element => { await addBookmarkToFolder(currentPage._id, item._id); } mutateUserBookmarks(); - mutateBookmarkData(); + mutateBookmarkFolders(); setSelectedItem(item._id); mutateBookmarkInfo(); } catch (err) { toastError(err); } - }, [onSelectedChild, isBookmarked, currentPage, mutateUserBookmarks, mutateBookmarkData, mutateBookmarkInfo]); + }, [onSelectedChild, isBookmarked, currentPage, mutateUserBookmarks, mutateBookmarkFolders, mutateBookmarkInfo]); const renderBookmarkSubMenuItem = useCallback(() => { if (!isOpen) { From 20b3ca5400fb032f01abd77c68031c0a6ed18a72 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Thu, 27 Apr 2023 08:56:14 +0000 Subject: [PATCH 10/14] fix lint error --- apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx index 9f6b1c53136..c3c8829c370 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx @@ -161,6 +161,11 @@ export const BookmarkFolderMenuItem: React.FC<{ onSelectedChild={() => setSelectedItem(null)} item={child} isSelected={selectedItem === child._id} + currentPage={currentPage} + bookmarkInfo={bookmarkInfo} + mutateBookmarkFolders={mutateBookmarkFolders} + mutateBookmarkInfo={mutateBookmarkInfo} + mutateUserBookmarks={mutateUserBookmarks} />
From 0829cfc3eb0b6f6587192764dece3e4c77ee8ac6 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Fri, 28 Apr 2023 01:03:31 +0000 Subject: [PATCH 11/14] refactor bookmark menu and bookmark list --- .../Bookmarks/BookmarkFolderMenu.tsx | 64 ++++++++-- .../Bookmarks/BookmarkFolderMenuItem.tsx | 110 ++++-------------- .../Bookmarks/BookmarkMoveToRootBtn.tsx | 7 +- .../src/components/PageList/BookmarkList.tsx | 5 +- .../src/components/Sidebar/PageTree/Item.tsx | 2 +- 5 files changed, 85 insertions(+), 103 deletions(-) diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx index c8c81264c32..3ad91afa387 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx @@ -10,8 +10,10 @@ import { import { addBookmarkToFolder, addNewFolder, toggleBookmark } from '~/client/util/bookmark-utils'; import { toastError } from '~/client/util/toastr'; import { BookmarkFolderItems } from '~/interfaces/bookmark-info'; +import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui'; import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; +import { useBookmarkFolderDeleteModal } from '~/stores/modal'; import { useSWRxCurrentPage, useSWRxPageInfo } from '~/stores/page'; import { FolderIcon } from '../Icons/FolderIcon'; @@ -31,6 +33,7 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi const { data: bookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); const { mutate: mutatePageInfo } = useSWRxPageInfo(currentPage?._id); + const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal(); const isBookmarked = bookmarkInfo?.isBookmarked ?? false; @@ -99,9 +102,9 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi return bookmarkFolders != null && bookmarkFolders.length > 0; }, [bookmarkFolders]); - const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { + const onPressEnterHandlerForCreate = useCallback(async(folderName: string, item?: BookmarkFolderItems) => { try { - await addNewFolder(folderName, null); + await addNewFolder(folderName, item ? item._id : null); await mutateBookmarkFolders(); setIsCreateAction(false); } @@ -129,6 +132,47 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi setSelectedItem(itemId); }, [mutateBookmarkFolders, isBookmarked, currentPage, mutateBookmarkInfo, mutateUserBookmarks, toggleBookmarkHandler]); + // Delete folder handler + const onClickDeleteHandler = useCallback(async(e, item) => { + e.stopPropagation(); + + const bookmarkFolderDeleteHandler: onDeletedBookmarkFolderFunction = (folderId) => { + if (typeof folderId !== 'string') { + return; + } + mutateBookmarkInfo(); + mutateBookmarkFolders(); + }; + + if (item == null) { + return; + } + openDeleteBookmarkFolderModal(item, { onDeleted: bookmarkFolderDeleteHandler }); + }, [mutateBookmarkFolders, mutateBookmarkInfo, openDeleteBookmarkFolderModal]); + + const onClickChildMenuItemHandler = useCallback(async(e, item) => { + e.stopPropagation(); + + setSelectedItem(null); + + try { + if (isBookmarked && currentPage != null) { + await toggleBookmark(currentPage._id, isBookmarked); + } + if (currentPage != null) { + await addBookmarkToFolder(currentPage._id, item._id); + } + mutateUserBookmarks(); + mutateBookmarkFolders(); + setSelectedItem(item._id); + mutateBookmarkInfo(); + } + catch (err) { + toastError(err); + } + }, [isBookmarked, currentPage, mutateUserBookmarks, mutateBookmarkFolders, mutateBookmarkInfo]); + + const renderBookmarkMenuItem = (child?: BookmarkFolderItems[]) => { const renderSubmenu = () => { if (child == null || currentPage == null || bookmarkInfo == null) { @@ -147,12 +191,10 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi setSelectedItem(null)} currentPage={currentPage} - bookmarkInfo={bookmarkInfo} - mutateBookmarkFolders={mutateBookmarkFolders} - mutateBookmarkInfo={mutateBookmarkInfo} - mutateUserBookmarks={mutateUserBookmarks} + onClickDeleteHandler={onClickDeleteHandler} + onClickChildMenuItemHandler={onClickChildMenuItemHandler} + onPressEnterHandlerForCreate={onPressEnterHandlerForCreate} /> {isOpen && renderSubmenu()} @@ -212,12 +254,10 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi setSelectedItem(null)} currentPage={currentPage} - bookmarkInfo={bookmarkInfo} - mutateBookmarkFolders={mutateBookmarkFolders} - mutateBookmarkInfo={mutateBookmarkInfo} - mutateUserBookmarks={mutateUserBookmarks} + onClickDeleteHandler={onClickDeleteHandler} + onClickChildMenuItemHandler={onClickChildMenuItemHandler} + onPressEnterHandlerForCreate={onPressEnterHandlerForCreate} /> {isOpen && renderSubmenu()} diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx index c3c8829c370..c6f1288e6cd 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx @@ -8,16 +8,9 @@ import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown, } from 'reactstrap'; -import type { KeyedMutator } from 'swr'; -import { - addBookmarkToFolder, addNewFolder, hasChildren, toggleBookmark, -} from '~/client/util/bookmark-utils'; -import { toastError } from '~/client/util/toastr'; -import { BookmarkFolderItems, IBookmarkInfo } from '~/interfaces/bookmark-info'; -import { IPageHasId } from '~/interfaces/page'; -import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui'; -import { useBookmarkFolderDeleteModal } from '~/stores/modal'; +import { hasChildren } from '~/client/util/bookmark-utils'; +import { BookmarkFolderItems } from '~/interfaces/bookmark-info'; import { FolderIcon } from '../Icons/FolderIcon'; import { TriangleIcon } from '../Icons/TriangleIcon'; @@ -27,21 +20,17 @@ import { BookmarkFolderNameInput } from './BookmarkFolderNameInput'; export const BookmarkFolderMenuItem: React.FC<{ item: BookmarkFolderItems isSelected: boolean - onSelectedChild: () => void currentPage: IPagePopulatedToShowRevision - bookmarkInfo: IBookmarkInfo - mutateBookmarkFolders: KeyedMutator - mutateBookmarkInfo: KeyedMutator - mutateUserBookmarks: KeyedMutator + onClickDeleteHandler: (e: any, item: any) => Promise + onClickChildMenuItemHandler: (e: any, item: any) => Promise + onPressEnterHandlerForCreate: (folderName: string, item?: BookmarkFolderItems) => Promise }> = ({ item, isSelected, - onSelectedChild, currentPage, - bookmarkInfo, - mutateBookmarkFolders, - mutateBookmarkInfo, - mutateUserBookmarks, + onClickDeleteHandler, + onClickChildMenuItemHandler, + onPressEnterHandlerForCreate, }) => { const { t } = useTranslation(); @@ -49,23 +38,8 @@ export const BookmarkFolderMenuItem: React.FC<{ const [selectedItem, setSelectedItem] = useState(null); const [isCreateAction, setIsCreateAction] = useState(false); - const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal(); - - const isBookmarked = bookmarkInfo?.isBookmarked ?? false; - const childrenExists = hasChildren(item); - const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { - try { - await addNewFolder(folderName, item._id); - await mutateBookmarkFolders(); - setIsCreateAction(false); - } - catch (err) { - toastError(err); - } - }, [item._id, mutateBookmarkFolders]); - useEffect(() => { if (isOpen) { item.children?.forEach((bookmarkFolder) => { @@ -92,43 +66,6 @@ export const BookmarkFolderMenuItem: React.FC<{ setIsOpen(true); }, []); - // Delete folder handler - const onClickDeleteHandler = useCallback(async(e) => { - e.stopPropagation(); - const bookmarkFolderDeleteHandler: onDeletedBookmarkFolderFunction = (folderId) => { - if (typeof folderId !== 'string') { - return; - } - mutateBookmarkInfo(); - mutateBookmarkFolders(); - }; - - if (item == null) { - return; - } - openDeleteBookmarkFolderModal(item, { onDeleted: bookmarkFolderDeleteHandler }); - }, [item, mutateBookmarkFolders, mutateBookmarkInfo, openDeleteBookmarkFolderModal]); - - const onClickChildMenuItemHandler = useCallback(async(e, item) => { - e.stopPropagation(); - onSelectedChild(); - try { - if (isBookmarked && currentPage != null) { - await toggleBookmark(currentPage._id, isBookmarked); - } - if (currentPage != null) { - await addBookmarkToFolder(currentPage._id, item._id); - } - mutateUserBookmarks(); - mutateBookmarkFolders(); - setSelectedItem(item._id); - mutateBookmarkInfo(); - } - catch (err) { - toastError(err); - } - }, [onSelectedChild, isBookmarked, currentPage, mutateUserBookmarks, mutateBookmarkFolders, mutateBookmarkInfo]); - const renderBookmarkSubMenuItem = useCallback(() => { if (!isOpen) { return <>; @@ -158,29 +95,30 @@ export const BookmarkFolderMenuItem: React.FC<{ tabIndex={0} role="menuitem" onClick={e => onClickChildMenuItemHandler(e, child)}> setSelectedItem(null)} item={child} isSelected={selectedItem === child._id} currentPage={currentPage} - bookmarkInfo={bookmarkInfo} - mutateBookmarkFolders={mutateBookmarkFolders} - mutateBookmarkInfo={mutateBookmarkInfo} - mutateUserBookmarks={mutateUserBookmarks} + onClickDeleteHandler={onClickDeleteHandler} + onClickChildMenuItemHandler={onClickChildMenuItemHandler} + onPressEnterHandlerForCreate={onPressEnterHandlerForCreate} /> ))}
); - }, [isOpen, - isCreateAction, - onPressEnterHandlerForCreate, - t, - childrenExists, - item.children, - onClickNewBookmarkFolder, - selectedItem, - onClickChildMenuItemHandler, + }, [ + isOpen, + isCreateAction, + onPressEnterHandlerForCreate, + t, + childrenExists, + item.children, + onClickNewBookmarkFolder, + selectedItem, + currentPage, + onClickDeleteHandler, + onClickChildMenuItemHandler, ]); return ( @@ -210,7 +148,7 @@ export const BookmarkFolderMenuItem: React.FC<{ id={`bookmark-delete-button-${item._id}`} className="text-danger ml-auto" color="transparent" - onClick={e => onClickDeleteHandler(e)} + onClick={e => onClickDeleteHandler(e, item)} > diff --git a/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx b/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx index 43e84740b82..b8acb2095b1 100644 --- a/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx @@ -4,13 +4,14 @@ import { useTranslation } from 'react-i18next'; import { DropdownItem } from 'reactstrap'; export const BookmarkMoveToRootBtn: React.FC<{ - moveToRootClickedHandler: () => Promise -}> = React.memo(({ moveToRootClickedHandler }) => { + pageId: string + moveToRootClickedHandler: (pageId: string) => Promise +}> = React.memo(({ pageId, moveToRootClickedHandler }) => { const { t } = useTranslation(); return ( moveToRootClickedHandler(pageId)} className="grw-page-control-dropdown-item" data-testid="add-remove-bookmark-btn" > diff --git a/apps/app/src/components/PageList/BookmarkList.tsx b/apps/app/src/components/PageList/BookmarkList.tsx index 04aa88ecd81..ca7bbfe033f 100644 --- a/apps/app/src/components/PageList/BookmarkList.tsx +++ b/apps/app/src/components/PageList/BookmarkList.tsx @@ -124,7 +124,10 @@ export const BookmarkList = (props:Props): JSX.Element => { onClickBookmarkMenuItem={bookmarkMenuItemClickHandler} onClickRenameMenuItem={() => setIsRenameInputShown(true)} onClickDeleteMenuItem={deleteMenuItemClickHandler} - additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => ) : undefined} + additionalMenuItemOnTopRenderer={isMoveToRoot + ? () => + : undefined + } > diff --git a/apps/app/src/components/Sidebar/PageTree/Item.tsx b/apps/app/src/components/Sidebar/PageTree/Item.tsx index ec20bce1505..f3e3f8dec64 100644 --- a/apps/app/src/components/Sidebar/PageTree/Item.tsx +++ b/apps/app/src/components/Sidebar/PageTree/Item.tsx @@ -30,7 +30,7 @@ import { shouldRecoverPagePaths } from '~/utils/page-operation'; import ClosableTextInput from '../../Common/ClosableTextInput'; import CountBadge from '../../Common/CountBadge'; -import { MenuItemType, PageItemControl } from '../../Common/Dropdown/PageItemControl'; +import { PageItemControl } from '../../Common/Dropdown/PageItemControl'; import { ItemNode } from './ItemNode'; From 9beed7d3dab3bf5df25b16be1f847257f40c3da1 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Fri, 28 Apr 2023 02:11:44 +0000 Subject: [PATCH 12/14] refactor bookmark folder tree --- .../Bookmarks/BookmarkFolderItem.tsx | 102 +++++------------- .../Bookmarks/BookmarkFolderTree.tsx | 44 +++----- .../src/components/Bookmarks/BookmarkItem.tsx | 68 +++++------- .../Bookmarks/BookmarkMoveToRootBtn.tsx | 6 +- .../src/components/PageList/BookmarkList.tsx | 4 +- 5 files changed, 74 insertions(+), 150 deletions(-) diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx index 9b7c9151bfa..80d6656aa61 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx @@ -2,23 +2,20 @@ import { FC, useCallback, useState, } from 'react'; -import type { IPageHasId } from '@growi/core'; -import { useTranslation } from 'next-i18next'; import { DropdownToggle } from 'reactstrap'; -import type { KeyedMutator } from 'swr'; import { addBookmarkToFolder, addNewFolder, hasChildren, updateBookmarkFolder, } from '~/client/util/bookmark-utils'; -import { toastError, toastSuccess } from '~/client/util/toastr'; +import { toastError } from '~/client/util/toastr'; import { FolderIcon } from '~/components/Icons/FolderIcon'; import { TriangleIcon } from '~/components/Icons/TriangleIcon'; import { - BookmarkFolderItems, DragItemDataType, DragItemType, DRAG_ITEM_TYPE, IBookmarkInfo, + BookmarkFolderItems, DragItemDataType, DragItemType, DRAG_ITEM_TYPE, } from '~/interfaces/bookmark-info'; import { IPageToDeleteWithMeta } from '~/interfaces/page'; -import { onDeletedBookmarkFolderFunction, OnDeletedFunction } from '~/interfaces/ui'; -import { useBookmarkFolderDeleteModal, usePageDeleteModal } from '~/stores/modal'; +import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui'; +import { useBookmarkFolderDeleteModal } from '~/stores/modal'; import { BookmarkFolderItemControl } from './BookmarkFolderItemControl'; import { BookmarkFolderNameInput } from './BookmarkFolderNameInput'; @@ -31,23 +28,18 @@ type BookmarkFolderItemProps = { level: number root: string isUserHomePage?: boolean - bookmarkFolders: BookmarkFolderItems[] - mutateBookmarkFolders: KeyedMutator - userBookmarks: IPageHasId[] - mutateUserBookmarks: KeyedMutator - bookmarkInfo: IBookmarkInfo - mutateBookmarkInfo: KeyedMutator + onClickDeleteBookmarkHandler: (pageToDelete: IPageToDeleteWithMeta) => void + bookmarkFolderTreeMutation: () => void } export const BookmarkFolderItem: FC = (props: BookmarkFolderItemProps) => { const BASE_FOLDER_PADDING = 15; const acceptedTypes: DragItemType[] = [DRAG_ITEM_TYPE.FOLDER, DRAG_ITEM_TYPE.BOOKMARK]; const { - bookmarkFolder, isOpen: _isOpen = false, level, root, isUserHomePage, bookmarkFolders, - mutateBookmarkFolders, userBookmarks, mutateUserBookmarks, bookmarkInfo, mutateBookmarkInfo, + bookmarkFolder, isOpen: _isOpen = false, level, root, isUserHomePage, + onClickDeleteBookmarkHandler, bookmarkFolderTreeMutation, } = props; - const { t } = useTranslation(); const { name, _id: folderId, children, parent, bookmarks, } = bookmarkFolder; @@ -57,8 +49,6 @@ export const BookmarkFolderItem: FC = (props: BookmarkF const [isRenameAction, setIsRenameAction] = useState(false); const [isCreateAction, setIsCreateAction] = useState(false); - - const { open: openDeleteModal } = usePageDeleteModal(); const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal(); const childrenExists = hasChildren(children); @@ -70,17 +60,17 @@ export const BookmarkFolderItem: FC = (props: BookmarkF setTargetFolder(folderId); }, [folderId, isOpen]); - // Rename for bookmark folder handler + // Rename for bookmark folder handler const onPressEnterHandlerForRename = useCallback(async(folderName: string) => { try { await updateBookmarkFolder(folderId, folderName, parent); - mutateBookmarkFolders(); + bookmarkFolderTreeMutation(); setIsRenameAction(false); } catch (err) { toastError(err); } - }, [folderId, mutateBookmarkFolders, parent]); + }, [bookmarkFolderTreeMutation, folderId, parent]); // Create new folder / subfolder handler const onPressEnterHandlerForCreate = useCallback(async(folderName: string) => { @@ -88,13 +78,12 @@ export const BookmarkFolderItem: FC = (props: BookmarkF await addNewFolder(folderName, targetFolder); setIsOpen(true); setIsCreateAction(false); - mutateBookmarkFolders(); + bookmarkFolderTreeMutation(); } catch (err) { toastError(err); } - }, [mutateBookmarkFolders, targetFolder]); - + }, [bookmarkFolderTreeMutation, targetFolder]); const onClickPlusButton = useCallback(async(e) => { e.stopPropagation(); @@ -104,37 +93,12 @@ export const BookmarkFolderItem: FC = (props: BookmarkF setIsCreateAction(true); }, [childrenExists, isOpen]); - const onClickDeleteBookmarkHandler = useCallback((pageToDelete: IPageToDeleteWithMeta) => { - const pageDeletedHandler: OnDeletedFunction = (pathOrPathsToDelete, _isRecursively, isCompletely) => { - if (typeof pathOrPathsToDelete !== 'string') { - return; - } - const path = pathOrPathsToDelete; - - if (isCompletely) { - toastSuccess(t('deleted_pages_completely', { path })); - } - else { - toastSuccess(t('deleted_pages', { path })); - } - mutateBookmarkFolders(); - mutateBookmarkInfo(); - }; - openDeleteModal([pageToDelete], { onDeleted: pageDeletedHandler }); - }, [mutateBookmarkInfo, mutateBookmarkFolders, openDeleteModal, t]); - - const onUnbookmarkHandler = useCallback(() => { - mutateBookmarkFolders(); - mutateBookmarkInfo(); - }, [mutateBookmarkInfo, mutateBookmarkFolders]); - - const itemDropHandler = async(item: DragItemDataType, dragItemType: string | symbol | null) => { if (dragItemType === DRAG_ITEM_TYPE.FOLDER) { try { if (item.bookmarkFolder != null) { await updateBookmarkFolder(item.bookmarkFolder._id, item.bookmarkFolder.name, bookmarkFolder._id); - mutateBookmarkFolders(); + bookmarkFolderTreeMutation(); } } catch (err) { @@ -145,8 +109,7 @@ export const BookmarkFolderItem: FC = (props: BookmarkF try { if (item != null) { await addBookmarkToFolder(item._id, bookmarkFolder._id); - mutateBookmarkFolders(); - await mutateUserBookmarks(); + bookmarkFolderTreeMutation(); } } catch (err) { @@ -188,12 +151,8 @@ export const BookmarkFolderItem: FC = (props: BookmarkF level={level + 1} root={root} isUserHomePage={isUserHomePage} - bookmarkFolders={bookmarkFolders} - mutateBookmarkFolders={mutateBookmarkFolders} - userBookmarks={userBookmarks} - mutateUserBookmarks={mutateUserBookmarks} - bookmarkInfo={bookmarkInfo} - mutateBookmarkInfo={mutateBookmarkInfo} + onClickDeleteBookmarkHandler={onClickDeleteBookmarkHandler} + bookmarkFolderTreeMutation={bookmarkFolderTreeMutation} /> ); @@ -204,17 +163,13 @@ export const BookmarkFolderItem: FC = (props: BookmarkF return isOpen && bookmarks?.map((bookmark) => { return ( ); }); @@ -229,25 +184,24 @@ export const BookmarkFolderItem: FC = (props: BookmarkF if (typeof folderId !== 'string') { return; } - mutateBookmarkInfo(); - mutateBookmarkFolders(); + bookmarkFolderTreeMutation(); }; if (bookmarkFolder == null) { return; } openDeleteBookmarkFolderModal(bookmarkFolder, { onDeleted: bookmarkFolderDeleteHandler }); - }, [bookmarkFolder, mutateBookmarkFolders, mutateBookmarkInfo, openDeleteBookmarkFolderModal]); + }, [bookmarkFolder, bookmarkFolderTreeMutation, openDeleteBookmarkFolderModal]); - const onClickMoveToRootHandler = useCallback(async() => { + const onClickMoveToRootHandlerForBookmarkFolderItemControl = useCallback(async() => { try { await updateBookmarkFolder(bookmarkFolder._id, bookmarkFolder.name, null); - await mutateBookmarkFolders(); + bookmarkFolderTreeMutation(); } catch (err) { toastError(err); } - }, [bookmarkFolder._id, bookmarkFolder.name, mutateBookmarkFolders]); + }, [bookmarkFolder._id, bookmarkFolder.name, bookmarkFolderTreeMutation]); return (
@@ -300,7 +254,7 @@ export const BookmarkFolderItem: FC = (props: BookmarkF
e.stopPropagation()}> diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx index a5ae5e27184..a330bfbda50 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderTree.tsx @@ -3,9 +3,8 @@ import React, { useCallback } from 'react'; import { useTranslation } from 'next-i18next'; -import { toastError, toastSuccess } from '~/client/util/toastr'; -import { BookmarkFolderItems, DragItemType, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info'; -import { IPageHasId, IPageToDeleteWithMeta } from '~/interfaces/page'; +import { toastSuccess } from '~/client/util/toastr'; +import { IPageToDeleteWithMeta } from '~/interfaces/page'; import { OnDeletedFunction } from '~/interfaces/ui'; import { useSWRxCurrentUserBookmarks, useSWRBookmarkInfo } from '~/stores/bookmark'; import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder'; @@ -28,15 +27,16 @@ export const BookmarkFolderTree: React.FC<{isUserHomePage?: boolean}> = ({ isUse const { t } = useTranslation(); const { data: currentPage } = useSWRxCurrentPage(); - const { data: bookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); + const { mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id); const { data: bookmarkFolders, mutate: mutateBookmarkFolders } = useSWRxBookmarkFolderAndChild(); const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); const { open: openDeleteModal } = usePageDeleteModal(); - const onUnbookmarkHandler = useCallback(() => { + const bookmarkFolderTreeMutation = useCallback(() => { mutateUserBookmarks(); mutateBookmarkInfo(); - }, [mutateBookmarkInfo, mutateUserBookmarks]); + mutateBookmarkFolders(); + }, [mutateBookmarkFolders, mutateBookmarkInfo, mutateUserBookmarks]); const onClickDeleteBookmarkHandler = useCallback((pageToDelete: IPageToDeleteWithMeta) => { const pageDeletedHandler: OnDeletedFunction = (pathOrPathsToDelete, _isRecursively, isCompletely) => { @@ -44,12 +44,10 @@ export const BookmarkFolderTree: React.FC<{isUserHomePage?: boolean}> = ({ isUse toastSuccess(isCompletely ? t('deleted_pages_completely', { pathOrPathsToDelete }) : t('deleted_pages', { pathOrPathsToDelete })); - mutateUserBookmarks(); - mutateBookmarkInfo(); - mutateBookmarkFolders(); + bookmarkFolderTreeMutation(); }; openDeleteModal([pageToDelete], { onDeleted: pageDeletedHandler }); - }, [openDeleteModal, mutateUserBookmarks, mutateBookmarkInfo, mutateBookmarkFolders, t]); + }, [openDeleteModal, t, bookmarkFolderTreeMutation]); /* TODO: update in bookmarks folder v2. */ // const itemDropHandler = async(item: DragItemDataType, dragType: string | null | symbol) => { @@ -85,10 +83,6 @@ export const BookmarkFolderTree: React.FC<{isUserHomePage?: boolean}> = ({ isUse // }; - if (bookmarkFolders == null || userBookmarks == null || currentPage == null || bookmarkInfo == null) { - return <>; - } - return (
    @@ -101,29 +95,21 @@ export const BookmarkFolderTree: React.FC<{isUserHomePage?: boolean}> = ({ isUse level={0} root={bookmarkFolder._id} isUserHomePage={isUserHomePage} - bookmarkFolders={bookmarkFolders} - mutateBookmarkFolders={mutateBookmarkFolders} - userBookmarks={userBookmarks} - mutateUserBookmarks={mutateUserBookmarks} - bookmarkInfo={bookmarkInfo} - mutateBookmarkInfo={mutateBookmarkInfo} + onClickDeleteBookmarkHandler={onClickDeleteBookmarkHandler} + bookmarkFolderTreeMutation={bookmarkFolderTreeMutation} /> ); })} {userBookmarks?.map(userBookmark => (
    ))} diff --git a/apps/app/src/components/Bookmarks/BookmarkItem.tsx b/apps/app/src/components/Bookmarks/BookmarkItem.tsx index 7fbdc89c07b..77f28ca500b 100644 --- a/apps/app/src/components/Bookmarks/BookmarkItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkItem.tsx @@ -1,13 +1,10 @@ -import React, { - useCallback, useEffect, useState, useMemo, -} from 'react'; +import React, { useCallback, useState } from 'react'; import nodePath from 'path'; import { DevidedPagePath, pathUtils } from '@growi/core'; import { useTranslation } from 'react-i18next'; import { UncontrolledTooltip, DropdownToggle } from 'reactstrap'; -import type { KeyedMutator } from 'swr'; import { unbookmark } from '~/client/services/page-operation'; import { addBookmarkToFolder, renamePage } from '~/client/util/bookmark-utils'; @@ -26,70 +23,55 @@ import { DragAndDropWrapper } from './DragAndDropWrapper'; type Props = { bookmarkedPage: IPageHasId, - onUnbookmarked: () => void, - onRenamed: () => void, - onClickDeleteMenuItem: (pageToDelete: IPageToDeleteWithMeta) => void, - parentFolder: BookmarkFolderItems | null, level: number, - bookmarkFolders: BookmarkFolderItems[], - mutateBookmarkFolders: KeyedMutator, - userBookmarks: IPageHasId[], - mutateUserBookmarks: KeyedMutator, + parentFolder: BookmarkFolderItems | null, + isMoveToRoot: boolean, + onClickDeleteBookmarkHandler: (pageToDelete: IPageToDeleteWithMeta) => void, + bookmarkFolderTreeMutation: () => void } export const BookmarkItem = (props: Props): JSX.Element => { const BASE_FOLDER_PADDING = 15; const BASE_BOOKMARK_PADDING = 20; + const { t } = useTranslation(); + const { - bookmarkedPage, - onUnbookmarked, - onRenamed, - onClickDeleteMenuItem, parentFolder, level, - mutateBookmarkFolders, userBookmarks, mutateUserBookmarks, + bookmarkedPage, onClickDeleteBookmarkHandler, + parentFolder, level, isMoveToRoot, bookmarkFolderTreeMutation, } = props; + const [isRenameInputShown, setRenameInputShown] = useState(false); + + const { data: fetchedPageInfo } = useSWRxPageInfo(bookmarkedPage._id); + const dPagePath = new DevidedPagePath(bookmarkedPage.path, false, true); const { latter: pageTitle, former: formerPagePath } = dPagePath; const bookmarkItemId = `bookmark-item-${bookmarkedPage._id}`; - const { data: fetchedPageInfo } = useSWRxPageInfo(bookmarkedPage._id); - const paddingLeft = BASE_BOOKMARK_PADDING + (BASE_FOLDER_PADDING * (level + 1)); - const dragItem: Partial = { ...bookmarkedPage, parentFolder, }; - useEffect(() => { - mutateBookmarkFolders(); - }, [mutateBookmarkFolders]); - - const pageId = bookmarkedPage._id; - - const moveToRootClickedHandler = useCallback(async() => { + const onClickMoveToRootHandler = useCallback(async() => { try { - await addBookmarkToFolder(pageId, null); - await mutateUserBookmarks(); + await addBookmarkToFolder(bookmarkedPage._id, null); + bookmarkFolderTreeMutation(); } catch (err) { toastError(err); } - }, [mutateUserBookmarks, pageId]); - - const isMoveToRoot = useMemo(() => { - return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); - }, [pageId, userBookmarks]); + }, [bookmarkFolderTreeMutation, bookmarkedPage._id]); const bookmarkMenuItemClickHandler = useCallback(async() => { await unbookmark(bookmarkedPage._id); - onUnbookmarked(); - }, [onUnbookmarked, bookmarkedPage]); + bookmarkFolderTreeMutation(); + }, [bookmarkedPage._id, bookmarkFolderTreeMutation]); const renameMenuItemClickHandler = useCallback(() => { setRenameInputShown(true); }, []); - const pressEnterForRenameHandler = useCallback(async(inputText: string) => { const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(bookmarkedPage.path ?? '')); const newPagePath = nodePath.resolve(parentPath, inputText); @@ -101,13 +83,13 @@ export const BookmarkItem = (props: Props): JSX.Element => { try { setRenameInputShown(false); await renamePage(bookmarkedPage._id, bookmarkedPage.revision, newPagePath); - onRenamed(); + bookmarkFolderTreeMutation(); } catch (err) { setRenameInputShown(true); toastError(err); } - }, [bookmarkedPage, onRenamed]); + }, [bookmarkedPage, bookmarkFolderTreeMutation]); const deleteMenuItemClickHandler = useCallback(async(_pageId: string, pageInfo: IPageInfoAll | undefined): Promise => { if (bookmarkedPage._id == null || bookmarkedPage.path == null) { @@ -123,8 +105,8 @@ export const BookmarkItem = (props: Props): JSX.Element => { meta: pageInfo, }; - onClickDeleteMenuItem(pageToDelete); - }, [bookmarkedPage, onClickDeleteMenuItem]); + onClickDeleteBookmarkHandler(pageToDelete); + }, [bookmarkedPage._id, bookmarkedPage.path, bookmarkedPage.revision, onClickDeleteBookmarkHandler]); return ( { onClickBookmarkMenuItem={bookmarkMenuItemClickHandler} onClickRenameMenuItem={renameMenuItemClickHandler} onClickDeleteMenuItem={deleteMenuItemClickHandler} - additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => ) : undefined} + additionalMenuItemOnTopRenderer={isMoveToRoot + ? () => + : undefined} > diff --git a/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx b/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx index b8acb2095b1..513e420b346 100644 --- a/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx @@ -5,13 +5,13 @@ import { DropdownItem } from 'reactstrap'; export const BookmarkMoveToRootBtn: React.FC<{ pageId: string - moveToRootClickedHandler: (pageId: string) => Promise -}> = React.memo(({ pageId, moveToRootClickedHandler }) => { + onClickMoveToRootHandler: (pageId: string) => Promise +}> = React.memo(({ pageId, onClickMoveToRootHandler }) => { const { t } = useTranslation(); return ( moveToRootClickedHandler(pageId)} + onClick={() => onClickMoveToRootHandler(pageId)} className="grw-page-control-dropdown-item" data-testid="add-remove-bookmark-btn" > diff --git a/apps/app/src/components/PageList/BookmarkList.tsx b/apps/app/src/components/PageList/BookmarkList.tsx index ca7bbfe033f..c3d249bdb23 100644 --- a/apps/app/src/components/PageList/BookmarkList.tsx +++ b/apps/app/src/components/PageList/BookmarkList.tsx @@ -46,7 +46,7 @@ export const BookmarkList = (props:Props): JSX.Element => { return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); }, [pageId, userBookmarks]); - const moveToRootClickedHandler = useCallback(async() => { + const onClickMoveToRootHandler = useCallback(async() => { try { await addBookmarkToFolder(pageId, null); await mutateUserBookmarks(); @@ -125,7 +125,7 @@ export const BookmarkList = (props:Props): JSX.Element => { onClickRenameMenuItem={() => setIsRenameInputShown(true)} onClickDeleteMenuItem={deleteMenuItemClickHandler} additionalMenuItemOnTopRenderer={isMoveToRoot - ? () => + ? () => : undefined } > From e8e05751f5019137ba3868f004567aabcf0bd915 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Fri, 28 Apr 2023 11:48:42 +0000 Subject: [PATCH 13/14] apply feedback --- .../Bookmarks/BookmarkFolderItem.tsx | 9 +- .../Bookmarks/BookmarkFolderItemControl.tsx | 6 +- .../Bookmarks/BookmarkFolderTree.tsx | 2 +- .../src/components/Bookmarks/BookmarkItem.tsx | 6 +- .../src/components/PageList/BookmarkList.tsx | 139 ------------------ 5 files changed, 11 insertions(+), 151 deletions(-) delete mode 100644 apps/app/src/components/PageList/BookmarkList.tsx diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx index 80d6656aa61..261f3cb37c4 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderItem.tsx @@ -140,7 +140,6 @@ export const BookmarkFolderItem: FC = (props: BookmarkF return true; }; - const renderChildFolder = () => { return isOpen && children?.map((childFolder) => { return ( @@ -167,7 +166,7 @@ export const BookmarkFolderItem: FC = (props: BookmarkF bookmarkedPage={bookmark.page} level={level + 1} parentFolder={bookmarkFolder} - isMoveToRoot={true} + canMoveToRoot={true} onClickDeleteBookmarkHandler={onClickDeleteBookmarkHandler} bookmarkFolderTreeMutation={bookmarkFolderTreeMutation} /> @@ -254,8 +253,10 @@ export const BookmarkFolderItem: FC = (props: BookmarkF
    e.stopPropagation()}> diff --git a/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx b/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx index 8f70cea303b..7eb728d4f17 100644 --- a/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkFolderItemControl.tsx @@ -7,13 +7,11 @@ import { export const BookmarkFolderItemControl: React.FC<{ children?: React.ReactNode - isMoveToRoot: boolean - onClickMoveToRoot: () => void + onClickMoveToRoot?: () => Promise onClickRename: () => void onClickDelete: () => void }> = ({ children, - isMoveToRoot, onClickMoveToRoot, onClickRename, onClickDelete, @@ -33,7 +31,7 @@ export const BookmarkFolderItemControl: React.FC<{ container="body" style={{ zIndex: 1055 }} /* make it larger than $zindex-modal of bootstrap */ > - {isMoveToRoot && ( + {onClickMoveToRoot && ( = ({ isUse bookmarkedPage={userBookmark} level={0} parentFolder={null} - isMoveToRoot={false} + canMoveToRoot={false} onClickDeleteBookmarkHandler={onClickDeleteBookmarkHandler} bookmarkFolderTreeMutation={bookmarkFolderTreeMutation} /> diff --git a/apps/app/src/components/Bookmarks/BookmarkItem.tsx b/apps/app/src/components/Bookmarks/BookmarkItem.tsx index 77f28ca500b..df7e737b6c9 100644 --- a/apps/app/src/components/Bookmarks/BookmarkItem.tsx +++ b/apps/app/src/components/Bookmarks/BookmarkItem.tsx @@ -25,7 +25,7 @@ type Props = { bookmarkedPage: IPageHasId, level: number, parentFolder: BookmarkFolderItems | null, - isMoveToRoot: boolean, + canMoveToRoot: boolean, onClickDeleteBookmarkHandler: (pageToDelete: IPageToDeleteWithMeta) => void, bookmarkFolderTreeMutation: () => void } @@ -38,7 +38,7 @@ export const BookmarkItem = (props: Props): JSX.Element => { const { bookmarkedPage, onClickDeleteBookmarkHandler, - parentFolder, level, isMoveToRoot, bookmarkFolderTreeMutation, + parentFolder, level, canMoveToRoot, bookmarkFolderTreeMutation, } = props; const [isRenameInputShown, setRenameInputShown] = useState(false); @@ -138,7 +138,7 @@ export const BookmarkItem = (props: Props): JSX.Element => { onClickBookmarkMenuItem={bookmarkMenuItemClickHandler} onClickRenameMenuItem={renameMenuItemClickHandler} onClickDeleteMenuItem={deleteMenuItemClickHandler} - additionalMenuItemOnTopRenderer={isMoveToRoot + additionalMenuItemOnTopRenderer={canMoveToRoot ? () => : undefined} > diff --git a/apps/app/src/components/PageList/BookmarkList.tsx b/apps/app/src/components/PageList/BookmarkList.tsx deleted file mode 100644 index c3d249bdb23..00000000000 --- a/apps/app/src/components/PageList/BookmarkList.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useCallback, useState, useMemo } from 'react'; - -import nodePath from 'path'; - -import { - IPageInfoAll, IPageToDeleteWithMeta, pathUtils, -} from '@growi/core'; -import { useTranslation } from 'next-i18next'; -import { DropdownToggle } from 'reactstrap'; - -import { unbookmark } from '~/client/services/page-operation'; -import { apiv3Put } from '~/client/util/apiv3-client'; -import { addBookmarkToFolder } from '~/client/util/bookmark-utils'; -import { ValidationTarget } from '~/client/util/input-validator'; -import { toastError, toastSuccess } from '~/client/util/toastr'; -import { IPageHasId } from '~/interfaces/page'; -import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark'; -import loggerFactory from '~/utils/logger'; - -import { BookmarkMoveToRootBtn } from '../Bookmarks/BookmarkMoveToRootBtn'; -import ClosableTextInput from '../Common/ClosableTextInput'; -import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl'; - -import { PageListItemS } from './PageListItemS'; - -const logger = loggerFactory('growi:BookmarkList'); - -type Props = { - page: IPageHasId - onRenamed: () => void - onUnbookmarked: () => void - onClickDeleteMenuItem: (pageToDelete: IPageToDeleteWithMeta) => void -} -export const BookmarkList = (props:Props): JSX.Element => { - const { - page, onRenamed, onUnbookmarked, onClickDeleteMenuItem, - } = props; - const { t } = useTranslation(); - const [isRenameInputShown, setIsRenameInputShown] = useState(false); - - const pageId = page._id; - - const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks(); - - const isMoveToRoot = useMemo(() => { - return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId); - }, [pageId, userBookmarks]); - - const onClickMoveToRootHandler = useCallback(async() => { - try { - await addBookmarkToFolder(pageId, null); - await mutateUserBookmarks(); - } - catch (err) { - toastError(err); - } - }, [mutateUserBookmarks, pageId]); - - const bookmarkMenuItemClickHandler = useCallback(async() => { - await unbookmark(page._id); - onUnbookmarked(); - }, [page._id, onUnbookmarked]); - - const deleteMenuItemClickHandler = useCallback(async(_pageId: string, pageInfo: IPageInfoAll | undefined): Promise => { - if (page._id == null || page.path == null) { - throw Error('_id and path must not be null.'); - } - - const pageToDelete: IPageToDeleteWithMeta = { - data: { - _id: page._id, - revision: page.revision as string, - path: page.path, - }, - meta: pageInfo, - }; - - onClickDeleteMenuItem(pageToDelete); - }, [onClickDeleteMenuItem, page]); - - const pressEnterForRenameHandler = useCallback(async(inputText: string) => { - const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(page.path ?? '')); - const newPagePath = nodePath.resolve(parentPath, inputText); - if (newPagePath === page.path) { - setIsRenameInputShown(false); - return; - } - - try { - setIsRenameInputShown(false); - await apiv3Put('/pages/rename', { - pageId: page._id, - revisionId: page.revision, - newPagePath, - }); - onRenamed(); - toastSuccess(t('renamed_pages', { path: page.path })); - } - catch (err) { - setIsRenameInputShown(true); - logger.error('failed to fetch data', err); - toastError(err); - } - }, [onRenamed, page, t]); - - return ( -
  • - { isRenameInputShown ? ( - { setIsRenameInputShown(false) }} - onPressEnter={pressEnterForRenameHandler} - validationTarget={ValidationTarget.PAGE} - /> - ) : ( - - )} - - setIsRenameInputShown(true)} - onClickDeleteMenuItem={deleteMenuItemClickHandler} - additionalMenuItemOnTopRenderer={isMoveToRoot - ? () => - : undefined - } - > - - - - -
  • - - ); -}; From 1b6e811c78f148c66b49636d6697a56641af7b95 Mon Sep 17 00:00:00 2001 From: ryoji-s Date: Fri, 28 Apr 2023 11:52:40 +0000 Subject: [PATCH 14/14] clean code --- apps/app/src/components/UsersHomePageFooter.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/app/src/components/UsersHomePageFooter.tsx b/apps/app/src/components/UsersHomePageFooter.tsx index 97b317ee530..6b4b6ec385e 100644 --- a/apps/app/src/components/UsersHomePageFooter.tsx +++ b/apps/app/src/components/UsersHomePageFooter.tsx @@ -1,11 +1,9 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { useTranslation } from 'next-i18next'; - -import { apiv3Post } from '~/client/util/apiv3-client'; import { addNewFolder } from '~/client/util/bookmark-utils'; -import { toastError, toastSuccess } from '~/client/util/toastr'; +import { toastError } from '~/client/util/toastr'; import { RecentlyCreatedIcon } from '~/components/Icons/RecentlyCreatedIcon'; import { RecentCreated } from '~/components/RecentCreated/RecentCreated'; import styles from '~/components/UsersHomePageFooter.module.scss'; @@ -38,7 +36,7 @@ export const UsersHomePageFooter = (props: UsersHomePageFooterProps): JSX.Elemen catch (err) { toastError(err); } - }, [mutateChildBookmarkData, t]); + }, [mutateChildBookmarkData]); return (