diff options
| author | MohamedBassem <me@mbassem.com> | 2024-03-21 02:15:56 +0000 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-03-21 02:15:56 +0000 |
| commit | 7d7d3754d33b41478fea2d2d7ed902d665a9e03d (patch) | |
| tree | 59575d23a2e130be0be8d3a4da240153b0717d01 /apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx | |
| parent | 8f14e9f50fae51e09fdfb529af389a046b4672f6 (diff) | |
| download | karakeep-7d7d3754d33b41478fea2d2d7ed902d665a9e03d.tar.zst | |
feature: A better looking bookmark preview page
Diffstat (limited to 'apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx')
| -rw-r--r-- | apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx | 204 |
1 files changed, 145 insertions, 59 deletions
diff --git a/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx b/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx index 4209192e..632422c4 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkPreview.tsx @@ -2,58 +2,98 @@ import Image from "next/image"; import Link from "next/link"; -import { BackButton } from "@/components/ui/back-button"; +import { ScrollArea } from "@/components/ui/scroll-area"; import { Skeleton } from "@/components/ui/skeleton"; -import { isBookmarkStillCrawling } from "@/lib/bookmarkUtils"; +import { + Tooltip, + TooltipContent, + TooltipPortal, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { + isBookmarkStillCrawling, + isBookmarkStillLoading, +} from "@/lib/bookmarkUtils"; import { api } from "@/lib/trpc"; -import { ArrowLeftCircle, CalendarDays, ExternalLink } from "lucide-react"; +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"; -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 (isBookmarkStillCrawling(data)) { - return 1000; - } - return false; - }, - }, +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> ); +} - const linkHeader = bookmark.content.type == "link" && ( - <div className="flex flex-col space-y-2"> - <p className="text-center text-3xl"> - {bookmark.content.title ?? bookmark.content.url} - </p> - <Link href={bookmark.content.url} className="mx-auto flex gap-2"> +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> + <TooltipContent>{createdAt.toLocaleString()}</TooltipContent> + </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> + <hr /> </div> ); +} +function TextContentSection({ bookmark }: { bookmark: ZBookmark }) { let content; switch (bookmark.content.type) { case "link": { if (!bookmark.content.htmlContent) { content = ( - <div className="text-red-500">Failed to fetch link content ...</div> + <div className="text-destructive"> + Failed to fetch link content ... + </div> ); } else { content = ( @@ -61,24 +101,39 @@ export default function BookmarkPreview({ dangerouslySetInnerHTML={{ __html: bookmark.content.htmlContent || "", }} - className="prose" + className="prose mx-auto" /> ); } break; } case "text": { - content = <Markdown className="prose">{bookmark.content.text}</Markdown>; + content = ( + <Markdown className="prose mx-auto">{bookmark.content.text}</Markdown> + ); break; } - case "asset": { + } + + 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 w-full"> + <div className="relative h-full min-w-full"> <Image alt="asset" fill={true} + className="object-contain" src={`/api/assets/${bookmark.content.assetId}`} /> </div> @@ -88,31 +143,62 @@ export default function BookmarkPreview({ 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="m-4 min-h-screen space-y-4 rounded-md border bg-background p-4"> - <div className="flex justify-between"> - <BackButton className="ghost" variant="ghost"> - <ArrowLeftCircle /> - </BackButton> - <div className="my-auto"> - <span className="my-auto flex gap-2"> - <CalendarDays /> {bookmark.createdAt.toLocaleString()} - </span> - </div> + <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> - <hr /> - {linkHeader} - <div className="mx-auto flex h-full border-x p-2 px-4 lg:w-2/3"> - {isBookmarkStillCrawling(bookmark) ? ( - <div className="flex w-full flex-col gap-2"> - <Skeleton className="h-4" /> - <Skeleton className="h-4" /> - <Skeleton className="h-4" /> - </div> - ) : ( - content - )} + <div className="lg:col-span1 row-span-1 flex flex-col gap-4 bg-gray-100 p-4 lg:row-auto"> + {linkHeader} + <CreationTime createdAt={bookmark.createdAt} /> + <div className="flex gap-2"> + <TagsEditor bookmark={bookmark} /> + </div> </div> </div> ); |
