diff options
| author | MohamedBassem <me@mbassem.com> | 2024-04-07 18:30:00 +0100 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-04-07 19:00:00 +0100 |
| commit | 79d61be7e15dc5d23fb687a5f71e0097088a99ac (patch) | |
| tree | da72f19cdb74ef4ed2a75bcfddd13bdfb874f205 /apps/web/components/dashboard/bookmarks | |
| parent | 44918316007ed3153dc802a4b11db3ea09024a8b (diff) | |
| download | karakeep-79d61be7e15dc5d23fb687a5f71e0097088a99ac.tar.zst | |
feature: Extract hook logic into separate package and add a new action bar in bookmark preview
Diffstat (limited to 'apps/web/components/dashboard/bookmarks')
5 files changed, 53 insertions, 303 deletions
diff --git a/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx index d2beb8d4..420fed84 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx @@ -1,11 +1,12 @@ +import BookmarkPreview from "@/components/dashboard/preview/BookmarkPreview"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; -import { Maximize2, Star } from "lucide-react"; +import { Maximize2 } from "lucide-react"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; import BookmarkOptions from "./BookmarkOptions"; -import BookmarkPreview from "./BookmarkPreview"; +import { FavouritedActionIcon } from "./icons"; export default function BookmarkActionBar({ bookmark, @@ -15,11 +16,7 @@ export default function BookmarkActionBar({ return ( <div className="flex text-gray-500"> {bookmark.favourited && ( - <Star - className="m-1 size-8 rounded p-1" - color="#ebb434" - fill="#ebb434" - /> + <FavouritedActionIcon className="m-1 size-8 rounded p-1" favourited /> )} <Dialog> <DialogTrigger asChild> diff --git a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx index e95ec9a2..e3cfc796 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx @@ -11,24 +11,28 @@ import { import { useToast } from "@/components/ui/use-toast"; import { useClientConfig } from "@/lib/clientConfig"; import { BookmarkListContext } from "@/lib/hooks/list-context"; -import { api } from "@/lib/trpc"; import { - Archive, Link, List, ListX, MoreHorizontal, Pencil, RotateCw, - Star, Tags, Trash2, } from "lucide-react"; import type { ZBookmark, ZBookmarkedLink } from "@hoarder/trpc/types/bookmarks"; +import { + useDeleteBookmark, + useRecrawlBookmark, + useUpdateBookmark, +} from "@hoarder/shared-react/hooks//bookmarks"; +import { useRemoveBookmarkFromList } from "@hoarder/shared-react/hooks//lists"; import { useAddToListModal } from "./AddToListModal"; import { BookmarkedTextEditor } from "./BookmarkedTextEditor"; +import { ArchivedActionIcon, FavouritedActionIcon } from "./icons"; import { useTagModel } from "./TagModal"; export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { @@ -46,15 +50,6 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { const { listId } = useContext(BookmarkListContext); - const invalidateAllBookmarksCache = - api.useUtils().bookmarks.getBookmarks.invalidate; - - const invalidateBookmarkCache = - api.useUtils().bookmarks.getBookmark.invalidate; - - const invalidateSearchCache = - api.useUtils().bookmarks.searchBookmarks.invalidate; - const onError = () => { toast({ variant: "destructive", @@ -62,48 +57,35 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { description: "There was a problem with your request.", }); }; - const deleteBookmarkMutator = api.bookmarks.deleteBookmark.useMutation({ + const deleteBookmarkMutator = useDeleteBookmark({ onSuccess: () => { toast({ description: "The bookmark has been deleted!", }); }, onError, - onSettled: () => { - invalidateAllBookmarksCache(); - invalidateSearchCache(); - }, }); - const updateBookmarkMutator = api.bookmarks.updateBookmark.useMutation({ + const updateBookmarkMutator = useUpdateBookmark({ onSuccess: () => { toast({ description: "The bookmark has been updated!", }); }, onError, - onSettled: () => { - invalidateBookmarkCache({ bookmarkId: bookmark.id }); - invalidateAllBookmarksCache(); - invalidateSearchCache(); - }, }); - const crawlBookmarkMutator = api.bookmarks.recrawlBookmark.useMutation({ + const crawlBookmarkMutator = useRecrawlBookmark({ onSuccess: () => { toast({ description: "Re-fetch has been enqueued!", }); }, onError, - onSettled: () => { - invalidateBookmarkCache({ bookmarkId: bookmark.id }); - }, }); - const removeFromListMutator = api.lists.removeFromList.useMutation({ - onSuccess: (_resp, req) => { - invalidateAllBookmarksCache({ listId: req.listId }); + const removeFromListMutator = useRemoveBookmarkFromList({ + onSuccess: () => { toast({ description: "The bookmark has been deleted from the list", }); @@ -145,7 +127,10 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { }) } > - <Star className="mr-2 size-4" /> + <FavouritedActionIcon + className="mr-2 size-4" + favourited={bookmark.favourited} + /> <span>{bookmark.favourited ? "Un-favourite" : "Favourite"}</span> </DropdownMenuItem> <DropdownMenuItem @@ -157,7 +142,10 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) { }) } > - <Archive className="mr-2 size-4" /> + <ArchivedActionIcon + className="mr-2 size-4" + archived={bookmark.archived} + /> <span>{bookmark.archived ? "Un-archive" : "Archive"}</span> </DropdownMenuItem> {bookmark.content.type === "link" && ( diff --git a/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx b/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx deleted file mode 100644 index 102e0788..00000000 --- a/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx +++ /dev/null @@ -1,216 +0,0 @@ -"use client"; - -import Image from "next/image"; -import Link from "next/link"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { Separator } from "@/components/ui/separator"; -import { Skeleton } from "@/components/ui/skeleton"; -import { - Tooltip, - TooltipContent, - TooltipPortal, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; -import { - isBookmarkStillCrawling, - isBookmarkStillLoading, -} from "@/lib/bookmarkUtils"; -import { api } from "@/lib/trpc"; -import dayjs from "dayjs"; -import relativeTime from "dayjs/plugin/relativeTime"; -import { CalendarDays, ExternalLink } from "lucide-react"; -import Markdown from "react-markdown"; - -import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; - -import { NoteEditor } from "./NoteEditor"; -import { TagsEditor } from "./TagsEditor"; - -dayjs.extend(relativeTime); - -function ContentLoading() { - return ( - <div className="flex w-full flex-col gap-2"> - <Skeleton className="h-4" /> - <Skeleton className="h-4" /> - <Skeleton className="h-4" /> - </div> - ); -} - -function CreationTime({ createdAt }: { createdAt: Date }) { - return ( - <TooltipProvider> - <Tooltip delayDuration={0}> - <TooltipTrigger asChild> - <span className="flex w-fit gap-2"> - <CalendarDays /> {dayjs(createdAt).fromNow()} - </span> - </TooltipTrigger> - <TooltipPortal> - <TooltipContent>{createdAt.toLocaleString()}</TooltipContent> - </TooltipPortal> - </Tooltip> - </TooltipProvider> - ); -} - -function LinkHeader({ bookmark }: { bookmark: ZBookmark }) { - if (bookmark.content.type !== "link") { - throw new Error("Unexpected content type"); - } - - const title = bookmark.content.title ?? bookmark.content.url; - - return ( - <div className="flex w-full flex-col items-center justify-center space-y-3"> - <TooltipProvider> - <Tooltip> - <TooltipTrigger asChild> - <p className="line-clamp-2 text-center text-lg">{title}</p> - </TooltipTrigger> - <TooltipPortal> - <TooltipContent side="bottom" className="w-96"> - {title} - </TooltipContent> - </TooltipPortal> - </Tooltip> - </TooltipProvider> - <Link - href={bookmark.content.url} - className="mx-auto flex gap-2 text-gray-400" - > - <span className="my-auto">View Original</span> - <ExternalLink /> - </Link> - <Separator /> - </div> - ); -} - -function TextContentSection({ bookmark }: { bookmark: ZBookmark }) { - let content; - switch (bookmark.content.type) { - case "link": { - if (!bookmark.content.htmlContent) { - content = ( - <div className="text-destructive"> - Failed to fetch link content ... - </div> - ); - } else { - content = ( - <div - dangerouslySetInnerHTML={{ - __html: bookmark.content.htmlContent || "", - }} - className="prose mx-auto dark:prose-invert" - /> - ); - } - break; - } - case "text": { - content = ( - <Markdown className="prose mx-auto dark:prose-invert"> - {bookmark.content.text} - </Markdown> - ); - break; - } - } - - return <ScrollArea className="h-full">{content}</ScrollArea>; -} - -function AssetContentSection({ bookmark }: { bookmark: ZBookmark }) { - if (bookmark.content.type != "asset") { - throw new Error("Invalid content type"); - } - - let content; - switch (bookmark.content.assetType) { - case "image": { - switch (bookmark.content.assetType) { - case "image": { - content = ( - <div className="relative h-full min-w-full"> - <Image - alt="asset" - fill={true} - className="object-contain" - src={`/api/assets/${bookmark.content.assetId}`} - /> - </div> - ); - } - } - break; - } - } - return content; -} - -export default function BookmarkPreview({ - initialData, -}: { - initialData: ZBookmark; -}) { - const { data: bookmark } = api.bookmarks.getBookmark.useQuery( - { - bookmarkId: initialData.id, - }, - { - initialData, - refetchInterval: (query) => { - const data = query.state.data; - if (!data) { - return false; - } - // If the link is not crawled or not tagged - if (isBookmarkStillLoading(data)) { - return 1000; - } - return false; - }, - }, - ); - - let content; - switch (bookmark.content.type) { - case "link": - case "text": { - content = <TextContentSection bookmark={bookmark} />; - break; - } - case "asset": { - content = <AssetContentSection bookmark={bookmark} />; - break; - } - } - - const linkHeader = bookmark.content.type == "link" && ( - <LinkHeader bookmark={bookmark} /> - ); - - return ( - <div className="grid grid-rows-3 gap-2 overflow-hidden bg-background lg:grid-cols-3 lg:grid-rows-none"> - <div className="row-span-2 h-full w-full overflow-hidden p-2 md:col-span-2 lg:row-auto"> - {isBookmarkStillCrawling(bookmark) ? <ContentLoading /> : content} - </div> - <div className="lg:col-span1 row-span-1 flex flex-col gap-4 overflow-auto bg-accent p-4 lg:row-auto"> - {linkHeader} - <CreationTime createdAt={bookmark.createdAt} /> - <div className="flex gap-4"> - <p className="text-sm text-gray-400">Tags</p> - <TagsEditor bookmark={bookmark} /> - </div> - <div className="flex gap-4"> - <p className="text-sm text-gray-400">Note</p> - <NoteEditor bookmark={bookmark} /> - </div> - </div> - </div> - ); -} diff --git a/apps/web/components/dashboard/bookmarks/NoteEditor.tsx b/apps/web/components/dashboard/bookmarks/NoteEditor.tsx deleted file mode 100644 index d712d523..00000000 --- a/apps/web/components/dashboard/bookmarks/NoteEditor.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Textarea } from "@/components/ui/textarea"; -import { toast } from "@/components/ui/use-toast"; -import { useClientConfig } from "@/lib/clientConfig"; -import { api } from "@/lib/trpc"; - -import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; - -export function NoteEditor({ bookmark }: { bookmark: ZBookmark }) { - const demoMode = !!useClientConfig().demoMode; - - const invalidateBookmarkCache = - api.useUtils().bookmarks.getBookmark.invalidate; - - const updateBookmarkMutator = api.bookmarks.updateBookmark.useMutation({ - onSuccess: () => { - toast({ - description: "The bookmark has been updated!", - }); - }, - onError: () => { - toast({ - description: "Something went wrong while saving the note", - variant: "destructive", - }); - }, - onSettled: () => { - invalidateBookmarkCache({ bookmarkId: bookmark.id }); - }, - }); - - return ( - <Textarea - className="h-44 w-full overflow-auto rounded bg-background p-2 text-sm text-gray-400 dark:text-gray-300" - defaultValue={bookmark.note ?? ""} - disabled={demoMode} - placeholder="Write some notes ..." - onBlur={(e) => { - if (e.currentTarget.value == bookmark.note) { - return; - } - updateBookmarkMutator.mutate({ - bookmarkId: bookmark.id, - note: e.currentTarget.value, - }); - }} - /> - ); -} diff --git a/apps/web/components/dashboard/bookmarks/icons.tsx b/apps/web/components/dashboard/bookmarks/icons.tsx new file mode 100644 index 00000000..d899f19d --- /dev/null +++ b/apps/web/components/dashboard/bookmarks/icons.tsx @@ -0,0 +1,29 @@ +import { Archive, ArchiveRestore, Star } from "lucide-react"; + +export function FavouritedActionIcon({ + favourited, + className, +}: { + favourited: boolean; + className?: string; +}) { + return favourited ? ( + <Star className={className} color="#ebb434" fill="#ebb434" /> + ) : ( + <Star className={className} /> + ); +} + +export function ArchivedActionIcon({ + archived, + className, +}: { + archived: boolean; + className?: string; +}) { + return archived ? ( + <ArchiveRestore className={className} /> + ) : ( + <Archive className={className} /> + ); +} |
