From 3208dda3848ad739f54cebf44c423e2b68e85b2d Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Wed, 28 Feb 2024 20:45:28 +0000 Subject: feature: Add support for storing and previewing raw notes --- .../dashboard/bookmarks/components/AddBookmark.tsx | 96 ++++++++++++++++++ .../app/dashboard/bookmarks/components/AddLink.tsx | 71 -------------- .../bookmarks/components/BookmarkOptions.tsx | 68 +++++++++---- .../bookmarks/components/BookmarkedTextEditor.tsx | 108 +++++++++++++++++++++ .../bookmarks/components/BookmarksGrid.tsx | 20 +++- .../dashboard/bookmarks/components/LinkCard.tsx | 50 +++------- .../app/dashboard/bookmarks/components/TagList.tsx | 40 ++++++++ .../dashboard/bookmarks/components/TagModal.tsx | 5 +- .../dashboard/bookmarks/components/TextCard.tsx | 62 ++++++++++++ packages/web/app/dashboard/bookmarks/layout.tsx | 4 +- packages/web/components/ui/textarea.tsx | 24 +++++ packages/web/lib/types/api/bookmarks.ts | 7 ++ packages/web/server/api/routers/bookmarks.test.ts | 36 +++++-- packages/web/server/api/routers/bookmarks.ts | 89 +++++++++++++---- 14 files changed, 510 insertions(+), 170 deletions(-) create mode 100644 packages/web/app/dashboard/bookmarks/components/AddBookmark.tsx delete mode 100644 packages/web/app/dashboard/bookmarks/components/AddLink.tsx create mode 100644 packages/web/app/dashboard/bookmarks/components/BookmarkedTextEditor.tsx create mode 100644 packages/web/app/dashboard/bookmarks/components/TagList.tsx create mode 100644 packages/web/app/dashboard/bookmarks/components/TextCard.tsx create mode 100644 packages/web/components/ui/textarea.tsx (limited to 'packages/web') diff --git a/packages/web/app/dashboard/bookmarks/components/AddBookmark.tsx b/packages/web/app/dashboard/bookmarks/components/AddBookmark.tsx new file mode 100644 index 00000000..4f0de87a --- /dev/null +++ b/packages/web/app/dashboard/bookmarks/components/AddBookmark.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Form, FormControl, FormField, FormItem } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Pencil, Plus } from "lucide-react"; +import { useForm, SubmitErrorHandler } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { toast } from "@/components/ui/use-toast"; +import { api } from "@/lib/trpc"; +import { ActionButton } from "@/components/ui/action-button"; +import { Button } from "@/components/ui/button"; +import { BookmarkedTextEditor } from "./BookmarkedTextEditor"; +import { useState } from "react"; + +function AddText() { + const [isEditorOpen, setEditorOpen] = useState(false); + + return ( +
+ + +
+ ); +} + +function AddLink() { + const formSchema = z.object({ + url: z.string().url({ message: "The link must be a valid URL" }), + }); + const form = useForm>({ + resolver: zodResolver(formSchema), + }); + + const invalidateBookmarksCache = api.useUtils().bookmarks.invalidate; + const createBookmarkMutator = api.bookmarks.createBookmark.useMutation({ + onSuccess: () => { + invalidateBookmarksCache(); + }, + onError: () => { + toast({ description: "Something went wrong", variant: "destructive" }); + }, + }); + + const onError: SubmitErrorHandler> = (errors) => { + toast({ + description: Object.values(errors) + .map((v) => v.message) + .join("\n"), + variant: "destructive", + }); + }; + + return ( +
+ + createBookmarkMutator.mutate({ url: value.url, type: "link" }), + onError, + )} + > +
+ { + return ( + + + + + + ); + }} + /> + + + +
+
+ + ); +} + +export default function AddBookmark() { + return ( +
+ + +
+ ); +} diff --git a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx b/packages/web/app/dashboard/bookmarks/components/AddLink.tsx deleted file mode 100644 index 242a52a5..00000000 --- a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx +++ /dev/null @@ -1,71 +0,0 @@ -"use client"; - -import { Form, FormControl, FormField, FormItem } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { Plus } from "lucide-react"; -import { useForm, SubmitErrorHandler } from "react-hook-form"; -import { z } from "zod"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { toast } from "@/components/ui/use-toast"; -import { api } from "@/lib/trpc"; -import { ActionButton } from "@/components/ui/action-button"; - -const formSchema = z.object({ - url: z.string().url({ message: "The link must be a valid URL" }), -}); - -export default function AddLink() { - const form = useForm>({ - resolver: zodResolver(formSchema), - }); - - const invalidateBookmarksCache = api.useUtils().bookmarks.invalidate; - const bookmarkLinkMutator = api.bookmarks.bookmarkLink.useMutation({ - onSuccess: () => { - invalidateBookmarksCache(); - }, - onError: () => { - toast({ description: "Something went wrong", variant: "destructive" }); - }, - }); - - const onError: SubmitErrorHandler> = (errors) => { - toast({ - description: Object.values(errors) - .map((v) => v.message) - .join("\n"), - variant: "destructive", - }); - }; - - return ( -
- - bookmarkLinkMutator.mutate({ url: value.url, type: "link" }), - onError, - )} - > -
- { - return ( - - - - - - ); - }} - /> - - - -
-
- - ); -} diff --git a/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx index b8f6d8f2..866a4cbd 100644 --- a/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx +++ b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx @@ -2,7 +2,7 @@ import { useToast } from "@/components/ui/use-toast"; import { api } from "@/lib/trpc"; -import { ZBookmark } from "@/lib/types/api/bookmarks"; +import { ZBookmark, ZBookmarkedLink } from "@/lib/types/api/bookmarks"; import { Button } from "@/components/ui/button"; import { DropdownMenu, @@ -14,12 +14,15 @@ import { Archive, Link, MoreHorizontal, + Pencil, RotateCw, Star, Tags, Trash2, } from "lucide-react"; import { useTagModel } from "./TagModal"; +import { useState } from "react"; +import { BookmarkedTextEditor } from "./BookmarkedTextEditor"; export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { const { toast } = useToast(); @@ -27,6 +30,8 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { const [_, setTagModalIsOpen, tagModal] = useTagModel(bookmark); + const [isTextEditorOpen, setTextEditorOpen] = useState(false); + const invalidateBookmarksCache = api.useUtils().bookmarks.invalidate; const onError = () => { @@ -72,13 +77,27 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { return ( <> {tagModal} + - + {bookmark.content.type === "text" && ( + setTextEditorOpen(true)}> + + Edit + + )} updateBookmarkMutator.mutate({ @@ -101,29 +120,36 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { {bookmark.archived ? "Un-archive" : "Archive"} - { - navigator.clipboard.writeText(bookmark.content.url); - toast({ - description: "Link was added to your clipboard!", - }); - }} - > - - Copy Link - + {bookmark.content.type === "link" && ( + { + navigator.clipboard.writeText( + (bookmark.content as ZBookmarkedLink).url, + ); + toast({ + description: "Link was added to your clipboard!", + }); + }} + > + + Copy Link + + )} setTagModalIsOpen(true)}> Edit Tags - - crawlBookmarkMutator.mutate({ bookmarkId: bookmark.id }) - } - > - - Refresh - + + {bookmark.content.type === "link" && ( + + crawlBookmarkMutator.mutate({ bookmarkId: bookmark.id }) + } + > + + Refresh + + )} diff --git a/packages/web/app/dashboard/bookmarks/components/BookmarkedTextEditor.tsx b/packages/web/app/dashboard/bookmarks/components/BookmarkedTextEditor.tsx new file mode 100644 index 00000000..e9138e03 --- /dev/null +++ b/packages/web/app/dashboard/bookmarks/components/BookmarkedTextEditor.tsx @@ -0,0 +1,108 @@ +import { ZBookmark } from "@/lib/types/api/bookmarks"; +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { ActionButton } from "@/components/ui/action-button"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { api } from "@/lib/trpc"; +import { useState } from "react"; +import { toast } from "@/components/ui/use-toast"; + +export function BookmarkedTextEditor({ + bookmark, + open, + setOpen, +}: { + bookmark?: ZBookmark; + open: boolean; + setOpen: (open: boolean) => void; +}) { + const isNewBookmark = bookmark === undefined; + const [noteText, setNoteText] = useState( + bookmark && bookmark.content.type == "text" ? bookmark.content.text : "", + ); + + const invalidateAllBookmarksCache = + api.useUtils().bookmarks.getBookmarks.invalidate; + const invalidateOneBookmarksCache = + api.useUtils().bookmarks.getBookmark.invalidate; + + const { mutate: createBookmarkMutator, isPending: isCreationPending } = + api.bookmarks.createBookmark.useMutation({ + onSuccess: () => { + invalidateAllBookmarksCache(); + toast({ + description: "Note created!", + }); + setOpen(false); + setNoteText(""); + }, + onError: () => { + toast({ description: "Something went wrong", variant: "destructive" }); + }, + }); + const { mutate: updateBookmarkMutator, isPending: isUpdatePending } = + api.bookmarks.updateBookmarkText.useMutation({ + onSuccess: () => { + invalidateOneBookmarksCache({ + bookmarkId: bookmark!.id, + }); + toast({ + description: "Note updated!", + }); + setOpen(false); + setNoteText(""); + }, + onError: () => { + toast({ description: "Something went wrong", variant: "destructive" }); + }, + }); + const isPending = isCreationPending || isUpdatePending; + + const onSave = () => { + if (isNewBookmark) { + createBookmarkMutator({ + type: "text", + text: noteText, + }); + } else { + updateBookmarkMutator({ + bookmarkId: bookmark.id, + text: noteText, + }); + } + }; + + return ( + + + + + {isNewBookmark ? "New Note" : "Edit Note"} + +