Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Update bookmarks tree move to root features #7586

64 changes: 52 additions & 12 deletions apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx
Original file line number Diff line number Diff line change
@@ -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
<BookmarkFolderMenuItem
item={folder}
isSelected={selectedItem === folder._id}
onSelectedChild={() => setSelectedItem(null)}
currentPage={currentPage}
bookmarkInfo={bookmarkInfo}
mutateBookmarkFolders={mutateBookmarkFolders}
mutateBookmarkInfo={mutateBookmarkInfo}
mutateUserBookmarks={mutateUserBookmarks}
onClickDeleteHandler={onClickDeleteHandler}
onClickChildMenuItemHandler={onClickChildMenuItemHandler}
onPressEnterHandlerForCreate={onPressEnterHandlerForCreate}
/>
{isOpen && renderSubmenu()}
</div>
@@ -212,12 +254,10 @@ export const BookmarkFolderMenu: React.FC<{children?: React.ReactNode}> = ({ chi
<BookmarkFolderMenuItem
item={folder}
isSelected={selectedItem === folder._id}
onSelectedChild={() => setSelectedItem(null)}
currentPage={currentPage}
bookmarkInfo={bookmarkInfo}
mutateBookmarkFolders={mutateBookmarkFolders}
mutateBookmarkInfo={mutateBookmarkInfo}
mutateUserBookmarks={mutateUserBookmarks}
onClickDeleteHandler={onClickDeleteHandler}
onClickChildMenuItemHandler={onClickChildMenuItemHandler}
onPressEnterHandlerForCreate={onPressEnterHandlerForCreate}
/>
{isOpen && renderSubmenu()}
</div>
110 changes: 24 additions & 86 deletions apps/app/src/components/Bookmarks/BookmarkFolderMenuItem.tsx
Original file line number Diff line number Diff line change
@@ -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,45 +20,26 @@ import { BookmarkFolderNameInput } from './BookmarkFolderNameInput';
export const BookmarkFolderMenuItem: React.FC<{
item: BookmarkFolderItems
isSelected: boolean
onSelectedChild: () => void
currentPage: IPagePopulatedToShowRevision
bookmarkInfo: IBookmarkInfo
mutateBookmarkFolders: KeyedMutator<BookmarkFolderItems[]>
mutateBookmarkInfo: KeyedMutator<IBookmarkInfo>
mutateUserBookmarks: KeyedMutator<IPageHasId[]>
onClickDeleteHandler: (e: any, item: any) => Promise<void>
onClickChildMenuItemHandler: (e: any, item: any) => Promise<void>
onPressEnterHandlerForCreate: (folderName: string, item?: BookmarkFolderItems) => Promise<void>
}> = ({
item,
isSelected,
onSelectedChild,
currentPage,
bookmarkInfo,
mutateBookmarkFolders,
mutateBookmarkInfo,
mutateUserBookmarks,
onClickDeleteHandler,
onClickChildMenuItemHandler,
onPressEnterHandlerForCreate,
}) => {
yuki-takei marked this conversation as resolved.
Show resolved Hide resolved
const { t } = useTranslation();

const [isOpen, setIsOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState<string | null>(null);
const [isCreateAction, setIsCreateAction] = useState<boolean>(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)}>
<BookmarkFolderMenuItem
onSelectedChild={() => setSelectedItem(null)}
item={child}
isSelected={selectedItem === child._id}
currentPage={currentPage}
bookmarkInfo={bookmarkInfo}
mutateBookmarkFolders={mutateBookmarkFolders}
mutateBookmarkInfo={mutateBookmarkInfo}
mutateUserBookmarks={mutateUserBookmarks}
onClickDeleteHandler={onClickDeleteHandler}
onClickChildMenuItemHandler={onClickChildMenuItemHandler}
onPressEnterHandlerForCreate={onPressEnterHandlerForCreate}
/>
</div>
</div>
))}
</DropdownMenu>
);
}, [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)}
>
<i className="icon-fw icon-trash grw-page-control-dropdown-icon"></i>
</DropdownToggle>
7 changes: 4 additions & 3 deletions apps/app/src/components/Bookmarks/BookmarkMoveToRootBtn.tsx
Original file line number Diff line number Diff line change
@@ -4,13 +4,14 @@ import { useTranslation } from 'react-i18next';
import { DropdownItem } from 'reactstrap';

export const BookmarkMoveToRootBtn: React.FC<{
moveToRootClickedHandler: () => Promise<void>
}> = React.memo(({ moveToRootClickedHandler }) => {
pageId: string
moveToRootClickedHandler: (pageId: string) => Promise<void>
}> = React.memo(({ pageId, moveToRootClickedHandler }) => {
const { t } = useTranslation();

return (
<DropdownItem
onClick={moveToRootClickedHandler}
onClick={() => moveToRootClickedHandler(pageId)}
className="grw-page-control-dropdown-item"
data-testid="add-remove-bookmark-btn"
>
5 changes: 4 additions & 1 deletion apps/app/src/components/PageList/BookmarkList.tsx
Original file line number Diff line number Diff line change
@@ -124,7 +124,10 @@ export const BookmarkList = (props:Props): JSX.Element => {
onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
onClickRenameMenuItem={() => setIsRenameInputShown(true)}
onClickDeleteMenuItem={deleteMenuItemClickHandler}
additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => <BookmarkMoveToRootBtn moveToRootClickedHandler={moveToRootClickedHandler}/>) : undefined}
additionalMenuItemOnTopRenderer={isMoveToRoot
? () => <BookmarkMoveToRootBtn pageId={pageId} moveToRootClickedHandler={moveToRootClickedHandler}/>
: undefined
}
>
<DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0 grw-visible-on-hover mr-1">
<i className="icon-options fa fa-rotate-90 p-1"></i>
2 changes: 1 addition & 1 deletion apps/app/src/components/Sidebar/PageTree/Item.tsx
Original file line number Diff line number Diff line change
@@ -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';