diff options
| author | MohamedBassem <me@mbassem.com> | 2024-03-01 18:00:58 +0000 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-03-01 18:00:58 +0000 |
| commit | 75d315dda4232ee3b89abf054f0b6ee10105ffe3 (patch) | |
| tree | f0796a136578f3b5aa82b4b3313e54fa3061ff5f /packages/web/app/dashboard/bookmarks/components | |
| parent | 588471d65039e6920751ac2add8874ee932bc2f1 (diff) | |
| download | karakeep-75d315dda4232ee3b89abf054f0b6ee10105ffe3.tar.zst | |
feature: Add support for creating and updating lists
Diffstat (limited to 'packages/web/app/dashboard/bookmarks/components')
| -rw-r--r-- | packages/web/app/dashboard/bookmarks/components/AddToListModal.tsx | 171 | ||||
| -rw-r--r-- | packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx | 11 |
2 files changed, 182 insertions, 0 deletions
diff --git a/packages/web/app/dashboard/bookmarks/components/AddToListModal.tsx b/packages/web/app/dashboard/bookmarks/components/AddToListModal.tsx new file mode 100644 index 00000000..36e32ab7 --- /dev/null +++ b/packages/web/app/dashboard/bookmarks/components/AddToListModal.tsx @@ -0,0 +1,171 @@ +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 { useState } from "react"; + +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import LoadingSpinner from "@/components/ui/spinner"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; + +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<z.infer<typeof formSchema>>({ + resolver: zodResolver(formSchema), + }); + + const { data: lists, isPending: isFetchingListsPending } = + api.lists.list.useQuery(); + + const listInvalidationFunction = api.useUtils().lists.get.invalidate; + const bookmarksInvalidationFunction = + api.useUtils().bookmarks.getBookmarks.invalidate; + + const { mutate: addToList, isPending: isAddingToListPending } = + api.lists.addToList.useMutation({ + onSuccess: (_resp, req) => { + toast({ + description: "List has been updated!", + }); + listInvalidationFunction({ listId: req.listId }); + bookmarksInvalidationFunction(); + }, + onError: (e) => { + if (e.data?.code == "BAD_REQUEST") { + toast({ + variant: "destructive", + description: e.message, + }); + } else { + toast({ + variant: "destructive", + title: "Something went wrong", + }); + } + }, + }); + + const isPending = isFetchingListsPending || isAddingToListPending; + + return ( + <Dialog open={open} onOpenChange={setOpen}> + <DialogContent> + <Form {...form}> + <form + onSubmit={form.handleSubmit((value) => { + addToList({ + bookmarkId: bookmarkId, + listId: value.listId, + }); + })} + > + <DialogHeader> + <DialogTitle>Add to List</DialogTitle> + </DialogHeader> + + <div className="py-4"> + {lists ? ( + <FormField + control={form.control} + name="listId" + render={({ field }) => { + return ( + <FormItem> + <FormControl> + <Select onValueChange={field.onChange}> + <SelectTrigger className="w-full"> + <SelectValue placeholder="Select a list" /> + </SelectTrigger> + <SelectContent> + <SelectGroup> + {lists && + lists.lists.map((l) => ( + <SelectItem key={l.id} value={l.id}> + {l.icon} {l.name} + </SelectItem> + ))} + </SelectGroup> + </SelectContent> + </Select> + </FormControl> + <FormMessage /> + </FormItem> + ); + }} + /> + ) : ( + <LoadingSpinner /> + )} + </div> + <DialogFooter className="sm:justify-end"> + <DialogClose asChild> + <Button type="button" variant="secondary"> + Close + </Button> + </DialogClose> + <ActionButton + type="submit" + loading={isAddingToListPending} + disabled={isPending} + > + Add + </ActionButton> + </DialogFooter> + </form> + </Form> + </DialogContent> + </Dialog> + ); +} + +export function useAddToListModal(bookmarkId: string) { + const [open, setOpen] = useState(false); + + return [ + open, + setOpen, + <AddToListModal + key={bookmarkId} + bookmarkId={bookmarkId} + open={open} + setOpen={setOpen} + />, + ] as const; +} diff --git a/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx index 3a2b6b35..d4447f29 100644 --- a/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx +++ b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx @@ -13,6 +13,7 @@ import { import { Archive, Link, + List, MoreHorizontal, Pencil, RotateCw, @@ -23,12 +24,16 @@ import { import { useTagModel } from "./TagModal"; import { useState } from "react"; import { BookmarkedTextEditor } from "./BookmarkedTextEditor"; +import { useAddToListModal } from "./AddToListModal"; export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { const { toast } = useToast(); const linkId = bookmark.id; const [_, setTagModalIsOpen, tagModal] = useTagModel(bookmark); + const [_2, setAddToListModalOpen, addToListModal] = useAddToListModal( + bookmark.id, + ); const [isTextEditorOpen, setTextEditorOpen] = useState(false); @@ -77,6 +82,7 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { return ( <> {tagModal} + {addToListModal} <BookmarkedTextEditor bookmark={bookmark} open={isTextEditorOpen} @@ -140,6 +146,11 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { <span>Edit Tags</span> </DropdownMenuItem> + <DropdownMenuItem onClick={() => setAddToListModalOpen(true)}> + <List className="mr-2 size-4" /> + <span>Add to List</span> + </DropdownMenuItem> + {bookmark.content.type === "link" && ( <DropdownMenuItem onClick={() => |
