|
@@ -1,30 +1,18 @@
|
|
|
-import React, {
|
|
|
|
|
- useCallback, useState,
|
|
|
|
|
-} from 'react';
|
|
|
|
|
-
|
|
|
|
|
|
|
+import React, { useCallback, useMemo, useState } from 'react';
|
|
|
|
|
+import { getCustomModifiers } from '@growi/ui/dist/utils';
|
|
|
import { useTranslation } from 'next-i18next';
|
|
import { useTranslation } from 'next-i18next';
|
|
|
-import {
|
|
|
|
|
- DropdownItem, DropdownMenu, UncontrolledDropdown,
|
|
|
|
|
-} from 'reactstrap';
|
|
|
|
|
|
|
+import { DropdownItem, DropdownMenu, UncontrolledDropdown } from 'reactstrap';
|
|
|
|
|
|
|
|
-import { addBookmarkToFolder, addNewFolder, toggleBookmark } from '~/client/util/bookmark-utils';
|
|
|
|
|
|
|
+import { addBookmarkToFolder, toggleBookmark } from '~/client/util/bookmark-utils';
|
|
|
import { toastError } from '~/client/util/toastr';
|
|
import { toastError } from '~/client/util/toastr';
|
|
|
-import { BookmarkFolderItems } from '~/interfaces/bookmark-info';
|
|
|
|
|
-import { onDeletedBookmarkFolderFunction } from '~/interfaces/ui';
|
|
|
|
|
import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark';
|
|
import { useSWRBookmarkInfo, useSWRxCurrentUserBookmarks } from '~/stores/bookmark';
|
|
|
import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder';
|
|
import { useSWRxBookmarkFolderAndChild } from '~/stores/bookmark-folder';
|
|
|
-import { useBookmarkFolderDeleteModal } from '~/stores/modal';
|
|
|
|
|
import { useSWRxCurrentPage, useSWRxPageInfo } from '~/stores/page';
|
|
import { useSWRxCurrentPage, useSWRxPageInfo } from '~/stores/page';
|
|
|
-
|
|
|
|
|
-import { FolderIcon } from '../Icons/FolderIcon';
|
|
|
|
|
-
|
|
|
|
|
import { BookmarkFolderMenuItem } from './BookmarkFolderMenuItem';
|
|
import { BookmarkFolderMenuItem } from './BookmarkFolderMenuItem';
|
|
|
-import { BookmarkFolderNameInput } from './BookmarkFolderNameInput';
|
|
|
|
|
|
|
|
|
|
export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ children }): JSX.Element => {
|
|
export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ children }): JSX.Element => {
|
|
|
const { t } = useTranslation();
|
|
const { t } = useTranslation();
|
|
|
|
|
|
|
|
- const [isCreateAction, setIsCreateAction] = useState(false);
|
|
|
|
|
const [selectedItem, setSelectedItem] = useState<string | null>(null);
|
|
const [selectedItem, setSelectedItem] = useState<string | null>(null);
|
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
|
|
|
@@ -33,10 +21,13 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi
|
|
|
const { data: bookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id);
|
|
const { data: bookmarkInfo, mutate: mutateBookmarkInfo } = useSWRBookmarkInfo(currentPage?._id);
|
|
|
const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks();
|
|
const { mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks();
|
|
|
const { mutate: mutatePageInfo } = useSWRxPageInfo(currentPage?._id);
|
|
const { mutate: mutatePageInfo } = useSWRxPageInfo(currentPage?._id);
|
|
|
- const { open: openDeleteBookmarkFolderModal } = useBookmarkFolderDeleteModal();
|
|
|
|
|
|
|
|
|
|
const isBookmarked = bookmarkInfo?.isBookmarked ?? false;
|
|
const isBookmarked = bookmarkInfo?.isBookmarked ?? false;
|
|
|
|
|
|
|
|
|
|
+ const isBookmarkFolderExists = useMemo((): boolean => {
|
|
|
|
|
+ return bookmarkFolders != null && bookmarkFolders.length > 0;
|
|
|
|
|
+ }, [bookmarkFolders]);
|
|
|
|
|
+
|
|
|
const toggleBookmarkHandler = useCallback(async() => {
|
|
const toggleBookmarkHandler = useCallback(async() => {
|
|
|
try {
|
|
try {
|
|
|
if (currentPage != null) {
|
|
if (currentPage != null) {
|
|
@@ -48,22 +39,19 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi
|
|
|
}
|
|
}
|
|
|
}, [currentPage, isBookmarked]);
|
|
}, [currentPage, isBookmarked]);
|
|
|
|
|
|
|
|
- const onClickNewBookmarkFolder = useCallback(() => {
|
|
|
|
|
- setIsCreateAction(true);
|
|
|
|
|
- }, []);
|
|
|
|
|
-
|
|
|
|
|
const onUnbookmarkHandler = useCallback(async() => {
|
|
const onUnbookmarkHandler = useCallback(async() => {
|
|
|
await toggleBookmarkHandler();
|
|
await toggleBookmarkHandler();
|
|
|
|
|
+ setIsOpen(false);
|
|
|
|
|
+ setSelectedItem(null);
|
|
|
mutateUserBookmarks();
|
|
mutateUserBookmarks();
|
|
|
mutateBookmarkInfo();
|
|
mutateBookmarkInfo();
|
|
|
mutateBookmarkFolders();
|
|
mutateBookmarkFolders();
|
|
|
mutatePageInfo();
|
|
mutatePageInfo();
|
|
|
- setSelectedItem(null);
|
|
|
|
|
}, [mutateBookmarkFolders, mutateBookmarkInfo, mutatePageInfo, mutateUserBookmarks, toggleBookmarkHandler]);
|
|
}, [mutateBookmarkFolders, mutateBookmarkInfo, mutatePageInfo, mutateUserBookmarks, toggleBookmarkHandler]);
|
|
|
|
|
|
|
|
const toggleHandler = useCallback(async() => {
|
|
const toggleHandler = useCallback(async() => {
|
|
|
setIsOpen(!isOpen);
|
|
setIsOpen(!isOpen);
|
|
|
- mutateBookmarkFolders();
|
|
|
|
|
|
|
+
|
|
|
if (isOpen && bookmarkFolders != null) {
|
|
if (isOpen && bookmarkFolders != null) {
|
|
|
bookmarkFolders.forEach((bookmarkFolder) => {
|
|
bookmarkFolders.forEach((bookmarkFolder) => {
|
|
|
bookmarkFolder.bookmarks.forEach((bookmark) => {
|
|
bookmarkFolder.bookmarks.forEach((bookmark) => {
|
|
@@ -73,13 +61,13 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
if (!isOpen && !isBookmarked) {
|
|
if (!isOpen && !isBookmarked) {
|
|
|
try {
|
|
try {
|
|
|
- toggleBookmarkHandler();
|
|
|
|
|
|
|
+ await toggleBookmarkHandler();
|
|
|
mutateUserBookmarks();
|
|
mutateUserBookmarks();
|
|
|
mutateBookmarkInfo();
|
|
mutateBookmarkInfo();
|
|
|
mutatePageInfo();
|
|
mutatePageInfo();
|
|
|
- setSelectedItem(null);
|
|
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
toastError(err);
|
|
toastError(err);
|
|
@@ -98,22 +86,11 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi
|
|
|
mutatePageInfo,
|
|
mutatePageInfo,
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
- const isBookmarkFolderExists = useCallback((): boolean => {
|
|
|
|
|
- return bookmarkFolders != null && bookmarkFolders.length > 0;
|
|
|
|
|
- }, [bookmarkFolders]);
|
|
|
|
|
|
|
+ const onMenuItemClickHandler = useCallback(async(e, itemId: string | null) => {
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
|
|
|
- const onPressEnterHandlerForCreate = useCallback(async(folderName: string, item?: BookmarkFolderItems) => {
|
|
|
|
|
- try {
|
|
|
|
|
- await addNewFolder(folderName, item ? item._id : null);
|
|
|
|
|
- await mutateBookmarkFolders();
|
|
|
|
|
- setIsCreateAction(false);
|
|
|
|
|
- }
|
|
|
|
|
- catch (err) {
|
|
|
|
|
- toastError(err);
|
|
|
|
|
- }
|
|
|
|
|
- }, [mutateBookmarkFolders]);
|
|
|
|
|
|
|
+ setSelectedItem(itemId);
|
|
|
|
|
|
|
|
- const onMenuItemClickHandler = useCallback(async(itemId: string) => {
|
|
|
|
|
try {
|
|
try {
|
|
|
if (isBookmarked) {
|
|
if (isBookmarked) {
|
|
|
await toggleBookmarkHandler();
|
|
await toggleBookmarkHandler();
|
|
@@ -121,147 +98,72 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi
|
|
|
if (currentPage != null) {
|
|
if (currentPage != null) {
|
|
|
await addBookmarkToFolder(currentPage._id, itemId);
|
|
await addBookmarkToFolder(currentPage._id, itemId);
|
|
|
}
|
|
}
|
|
|
- mutateBookmarkInfo();
|
|
|
|
|
- mutateUserBookmarks();
|
|
|
|
|
- }
|
|
|
|
|
- catch (err) {
|
|
|
|
|
- toastError(err);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- mutateBookmarkFolders();
|
|
|
|
|
- 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();
|
|
mutateUserBookmarks();
|
|
|
mutateBookmarkFolders();
|
|
mutateBookmarkFolders();
|
|
|
- setSelectedItem(item._id);
|
|
|
|
|
mutateBookmarkInfo();
|
|
mutateBookmarkInfo();
|
|
|
}
|
|
}
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
toastError(err);
|
|
toastError(err);
|
|
|
}
|
|
}
|
|
|
- }, [isBookmarked, currentPage, mutateUserBookmarks, mutateBookmarkFolders, mutateBookmarkInfo]);
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- const renderBookmarkMenuItem = (child?: BookmarkFolderItems[]) => {
|
|
|
|
|
- const renderSubmenu = () => {
|
|
|
|
|
- if (child == null || currentPage == null || bookmarkInfo == null) {
|
|
|
|
|
- return <></>;
|
|
|
|
|
- }
|
|
|
|
|
- return (
|
|
|
|
|
- <div className="bookmark-folder-submenu">
|
|
|
|
|
- {child.map(folder => (
|
|
|
|
|
- <div key={folder._id}>
|
|
|
|
|
- <div
|
|
|
|
|
- className="dropdown-item grw-bookmark-folder-menu-item"
|
|
|
|
|
- tabIndex={0}
|
|
|
|
|
- role="menuitem"
|
|
|
|
|
- onClick={() => onMenuItemClickHandler(folder._id)}
|
|
|
|
|
- >
|
|
|
|
|
- <BookmarkFolderMenuItem
|
|
|
|
|
- item={folder}
|
|
|
|
|
- isSelected={selectedItem === folder._id}
|
|
|
|
|
- currentPage={currentPage}
|
|
|
|
|
- onClickDeleteHandler={onClickDeleteHandler}
|
|
|
|
|
- onClickChildMenuItemHandler={onClickChildMenuItemHandler}
|
|
|
|
|
- onPressEnterHandlerForCreate={onPressEnterHandlerForCreate}
|
|
|
|
|
- />
|
|
|
|
|
- {isOpen && renderSubmenu()}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- ))}
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ }, [mutateBookmarkFolders, isBookmarked, currentPage, mutateBookmarkInfo, mutateUserBookmarks, toggleBookmarkHandler]);
|
|
|
|
|
|
|
|
|
|
+ const renderBookmarkMenuItem = () => {
|
|
|
return (
|
|
return (
|
|
|
<>
|
|
<>
|
|
|
- {isBookmarked && (
|
|
|
|
|
|
|
+ <DropdownItem
|
|
|
|
|
+ toggle={false}
|
|
|
|
|
+ onClick={onUnbookmarkHandler}
|
|
|
|
|
+ className={'grw-bookmark-folder-menu-item text-danger'}
|
|
|
|
|
+ >
|
|
|
|
|
+ <i className="fa fa-bookmark"></i>{' '}
|
|
|
|
|
+ <span className="mx-2 ">
|
|
|
|
|
+ {t('bookmark_folder.cancel_bookmark')}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </DropdownItem>
|
|
|
|
|
+
|
|
|
|
|
+ {isBookmarkFolderExists && (
|
|
|
<>
|
|
<>
|
|
|
|
|
+ <DropdownItem divider />
|
|
|
<DropdownItem
|
|
<DropdownItem
|
|
|
toggle={false}
|
|
toggle={false}
|
|
|
- onClick={onUnbookmarkHandler}
|
|
|
|
|
- className={'grw-bookmark-folder-menu-item text-danger'}
|
|
|
|
|
|
|
+ onClick={e => onMenuItemClickHandler(e, null)}
|
|
|
>
|
|
>
|
|
|
- <i className="fa fa-bookmark"></i>{' '}
|
|
|
|
|
- <span className="mx-2 ">
|
|
|
|
|
- {t('bookmark_folder.cancel_bookmark')}
|
|
|
|
|
- </span>
|
|
|
|
|
|
|
+ {t('bookmark_folder.do_not_include_folder')}
|
|
|
</DropdownItem>
|
|
</DropdownItem>
|
|
|
<DropdownItem divider />
|
|
<DropdownItem divider />
|
|
|
- </>
|
|
|
|
|
- )}
|
|
|
|
|
-
|
|
|
|
|
- {isCreateAction ? (
|
|
|
|
|
- <div className="mx-2">
|
|
|
|
|
- <BookmarkFolderNameInput
|
|
|
|
|
- onClickOutside={() => setIsCreateAction(false)}
|
|
|
|
|
- onPressEnter={onPressEnterHandlerForCreate}
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <DropdownItem
|
|
|
|
|
- toggle={false}
|
|
|
|
|
- onClick={onClickNewBookmarkFolder}
|
|
|
|
|
- className="grw-bookmark-folder-menu-item"
|
|
|
|
|
- >
|
|
|
|
|
- <FolderIcon isOpen={false} />
|
|
|
|
|
- <span className="mx-2 ">{t('bookmark_folder.new_folder')}</span>
|
|
|
|
|
- </DropdownItem>
|
|
|
|
|
- )}
|
|
|
|
|
-
|
|
|
|
|
- {isBookmarkFolderExists() && currentPage != null && bookmarkInfo != null && (
|
|
|
|
|
- <>
|
|
|
|
|
- <DropdownItem divider />
|
|
|
|
|
{bookmarkFolders?.map(folder => (
|
|
{bookmarkFolders?.map(folder => (
|
|
|
- <div key={folder._id}>
|
|
|
|
|
- <div
|
|
|
|
|
- className="dropdown-item grw-bookmark-folder-menu-item"
|
|
|
|
|
- tabIndex={0}
|
|
|
|
|
- role="menuitem"
|
|
|
|
|
- onClick={() => onMenuItemClickHandler(folder._id)}
|
|
|
|
|
- >
|
|
|
|
|
- <BookmarkFolderMenuItem
|
|
|
|
|
- item={folder}
|
|
|
|
|
- isSelected={selectedItem === folder._id}
|
|
|
|
|
- currentPage={currentPage}
|
|
|
|
|
- onClickDeleteHandler={onClickDeleteHandler}
|
|
|
|
|
- onClickChildMenuItemHandler={onClickChildMenuItemHandler}
|
|
|
|
|
- onPressEnterHandlerForCreate={onPressEnterHandlerForCreate}
|
|
|
|
|
- />
|
|
|
|
|
- {isOpen && renderSubmenu()}
|
|
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div key={folder._id}>
|
|
|
|
|
+ <div
|
|
|
|
|
+ className="dropdown-item grw-bookmark-folder-menu-item list-group-item list-group-item-action border-0 py-0"
|
|
|
|
|
+ tabIndex={0}
|
|
|
|
|
+ role="menuitem"
|
|
|
|
|
+ onClick={e => onMenuItemClickHandler(e, folder._id)}
|
|
|
|
|
+ >
|
|
|
|
|
+ <BookmarkFolderMenuItem
|
|
|
|
|
+ item={folder}
|
|
|
|
|
+ isSelected={selectedItem === folder._id}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ <>
|
|
|
|
|
+ {folder.children?.map(child => (
|
|
|
|
|
+ <div key={child._id}>
|
|
|
|
|
+ <div
|
|
|
|
|
+ className='dropdown-item grw-bookmark-folder-menu-item list-group-item list-group-item-action border-0 py-0'
|
|
|
|
|
+ style={{ paddingLeft: '40px' }}
|
|
|
|
|
+ tabIndex={0}
|
|
|
|
|
+ role="menuitem"
|
|
|
|
|
+ onClick={e => onMenuItemClickHandler(e, child._id)}>
|
|
|
|
|
+ <BookmarkFolderMenuItem
|
|
|
|
|
+ item={child}
|
|
|
|
|
+ isSelected={selectedItem === child._id}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </>
|
|
|
|
|
+ </>
|
|
|
))}
|
|
))}
|
|
|
</>
|
|
</>
|
|
|
)}
|
|
)}
|
|
@@ -271,14 +173,18 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<UncontrolledDropdown
|
|
<UncontrolledDropdown
|
|
|
|
|
+ isOpen={isOpen}
|
|
|
onToggle={toggleHandler}
|
|
onToggle={toggleHandler}
|
|
|
- direction={ isBookmarkFolderExists() ? 'up' : 'down' }
|
|
|
|
|
- className='grw-bookmark-folder-dropdown'>
|
|
|
|
|
|
|
+ direction={isBookmarkFolderExists ? 'up' : 'down'}
|
|
|
|
|
+ className='grw-bookmark-folder-dropdown'
|
|
|
|
|
+ >
|
|
|
{children}
|
|
{children}
|
|
|
<DropdownMenu
|
|
<DropdownMenu
|
|
|
right
|
|
right
|
|
|
- className='grw-bookmark-folder-menu'
|
|
|
|
|
|
|
+ persist
|
|
|
positionFixed
|
|
positionFixed
|
|
|
|
|
+ className='grw-bookmark-folder-menu'
|
|
|
|
|
+ modifiers={getCustomModifiers(true)}
|
|
|
>
|
|
>
|
|
|
{ renderBookmarkMenuItem() }
|
|
{ renderBookmarkMenuItem() }
|
|
|
</DropdownMenu>
|
|
</DropdownMenu>
|