From 88b92d8eb2a203f7092314a5d9c9a7070b568b6d Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Tue, 23 Apr 2024 20:11:25 +0100 Subject: feature(web): Change the AddList modal to allow adding and removing bookmarks from lists --- .../dashboard/bookmarks/AddToListModal.tsx | 132 ------------- .../dashboard/bookmarks/BookmarkOptions.tsx | 12 +- .../dashboard/bookmarks/ManageListsModal.tsx | 207 +++++++++++++++++++++ .../dashboard/lists/BookmarkListSelector.tsx | 7 +- 4 files changed, 219 insertions(+), 139 deletions(-) delete mode 100644 apps/web/components/dashboard/bookmarks/AddToListModal.tsx create mode 100644 apps/web/components/dashboard/bookmarks/ManageListsModal.tsx (limited to 'apps/web/components/dashboard') diff --git a/apps/web/components/dashboard/bookmarks/AddToListModal.tsx b/apps/web/components/dashboard/bookmarks/AddToListModal.tsx deleted file mode 100644 index 3b8a6700..00000000 --- a/apps/web/components/dashboard/bookmarks/AddToListModal.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { useState } from "react"; -import { ActionButton } from "@/components/ui/action-button"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogClose, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { - Form, - FormControl, - FormField, - FormItem, - FormMessage, -} from "@/components/ui/form"; -import { toast } from "@/components/ui/use-toast"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; - -import { useAddBookmarkToList } from "@hoarder/shared-react/hooks/lists"; - -import { BookmarkListSelector } from "../lists/BookmarkListSelector"; - -export default function AddToListModal({ - bookmarkId, - open, - setOpen, -}: { - bookmarkId: string; - open: boolean; - setOpen: (open: boolean) => void; -}) { - const formSchema = z.object({ - listId: z.string({ - required_error: "Please select a list", - }), - }); - const form = useForm>({ - resolver: zodResolver(formSchema), - }); - - const { mutate: addToList, isPending: isAddingToListPending } = - useAddBookmarkToList({ - onSuccess: () => { - toast({ - description: "List has been updated!", - }); - setOpen(false); - }, - onError: (e) => { - if (e.data?.code == "BAD_REQUEST") { - toast({ - variant: "destructive", - description: e.message, - }); - } else { - toast({ - variant: "destructive", - title: "Something went wrong", - }); - } - }, - }); - - return ( - - -
- { - addToList({ - bookmarkId: bookmarkId, - listId: value.listId, - }); - })} - > - - Add to List - - -
- { - return ( - - - - - - - ); - }} - /> -
- - - - - - Add - - -
- -
-
- ); -} - -export function useAddToListModal(bookmarkId: string) { - const [open, setOpen] = useState(false); - - return { - open, - setOpen, - content: ( - - ), - }; -} diff --git a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx index 6f07107b..e5f38eae 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx @@ -33,9 +33,9 @@ import { import { useRemoveBookmarkFromList } from "@hoarder/shared-react/hooks//lists"; import { useBookmarkGridContext } from "@hoarder/shared-react/hooks/bookmark-grid-context"; -import { useAddToListModal } from "./AddToListModal"; import { BookmarkedTextEditor } from "./BookmarkedTextEditor"; import { ArchivedActionIcon, FavouritedActionIcon } from "./icons"; +import { useManageListsModal } from "./ManageListsModal"; import { useTagModel } from "./TagModal"; export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { @@ -46,8 +46,8 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { const { setOpen: setTagModalIsOpen, content: tagModal } = useTagModel(bookmark); - const { setOpen: setAddToListModalOpen, content: addToListModal } = - useAddToListModal(bookmark.id); + const { setOpen: setManageListsModalOpen, content: manageListsModal } = + useManageListsModal(bookmark.id); const [isTextEditorOpen, setTextEditorOpen] = useState(false); @@ -99,7 +99,7 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { return ( <> {tagModal} - {addToListModal} + {manageListsModal} Edit Tags - setAddToListModalOpen(true)}> + setManageListsModalOpen(true)}> - Add to List + Manage Lists {listId && ( diff --git a/apps/web/components/dashboard/bookmarks/ManageListsModal.tsx b/apps/web/components/dashboard/bookmarks/ManageListsModal.tsx new file mode 100644 index 00000000..a906aee8 --- /dev/null +++ b/apps/web/components/dashboard/bookmarks/ManageListsModal.tsx @@ -0,0 +1,207 @@ +import { useState } from "react"; +import { ActionButton } from "@/components/ui/action-button"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from "@/components/ui/form"; +import { toast } from "@/components/ui/use-toast"; +import { api } from "@/lib/trpc"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { X } from "lucide-react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +import { + useAddBookmarkToList, + useBookmarkLists, + useRemoveBookmarkFromList, +} from "@hoarder/shared-react/hooks/lists"; + +import { BookmarkListSelector } from "../lists/BookmarkListSelector"; + +export default function ManageListsModal({ + bookmarkId, + open, + setOpen, +}: { + bookmarkId: string; + open: boolean; + setOpen: (open: boolean) => void; +}) { + const formSchema = z.object({ + listId: z.string({ + required_error: "Please select a list", + }), + }); + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + listId: undefined, + }, + }); + + const { data: allLists } = useBookmarkLists(undefined, { enabled: open }); + + const { data: alreadyInList } = api.lists.getListsOfBookmark.useQuery( + { + bookmarkId, + }, + { enabled: open }, + ); + + const { mutate: addToList, isPending: isAddingToListPending } = + useAddBookmarkToList({ + onSuccess: () => { + toast({ + description: "List has been updated!", + }); + form.resetField("listId"); + }, + onError: (e) => { + if (e.data?.code == "BAD_REQUEST") { + toast({ + variant: "destructive", + description: e.message, + }); + } else { + toast({ + variant: "destructive", + title: "Something went wrong", + }); + } + }, + }); + + const { mutate: deleteFromList, isPending: isDeleteFromListPending } = + useRemoveBookmarkFromList({ + onSuccess: () => { + toast({ + description: "List has been updated!", + }); + form.resetField("listId"); + }, + onError: (e) => { + if (e.data?.code == "BAD_REQUEST") { + toast({ + variant: "destructive", + description: e.message, + }); + } else { + toast({ + variant: "destructive", + title: "Something went wrong", + }); + } + }, + }); + + return ( + + +
+ { + addToList({ + bookmarkId: bookmarkId, + listId: value.listId, + }); + })} + > + + Manage Lists + + {allLists && ( +
    + {alreadyInList?.lists.map((list) => ( +
  • +

    + {allLists + .getPathById(list.id)! + .map((l) => `${l.icon} ${l.name}`) + .join(" / ")} +

    + + deleteFromList({ bookmarkId, listId: list.id }) + } + > + + +
  • + ))} +
+ )} + +
+ { + return ( + + + l.id, + )} + onChange={field.onChange} + /> + + + + ); + }} + /> +
+ + + + + + Add + + +
+ +
+
+ ); +} + +export function useManageListsModal(bookmarkId: string) { + const [open, setOpen] = useState(false); + + return { + open, + setOpen, + content: ( + + ), + }; +} diff --git a/apps/web/components/dashboard/lists/BookmarkListSelector.tsx b/apps/web/components/dashboard/lists/BookmarkListSelector.tsx index fdae1c17..144297cf 100644 --- a/apps/web/components/dashboard/lists/BookmarkListSelector.tsx +++ b/apps/web/components/dashboard/lists/BookmarkListSelector.tsx @@ -14,12 +14,14 @@ export function BookmarkListSelector({ value, onChange, hideSubtreeOf, + hideBookmarkIds = [], placeholder = "Select a list", }: { value?: string | null; onChange: (value: string) => void; placeholder?: string; hideSubtreeOf?: string; + hideBookmarkIds?: string[]; }) { const { data, isPending: isFetchingListsPending } = useBookmarkLists(); let { allPaths } = data ?? {}; @@ -29,6 +31,9 @@ export function BookmarkListSelector({ } allPaths = allPaths?.filter((path) => { + if (hideBookmarkIds.includes(path[path.length - 1].id)) { + return false; + } if (!hideSubtreeOf) { return true; } @@ -36,7 +41,7 @@ export function BookmarkListSelector({ }); return ( - -- cgit v1.2.3-70-g09d2