From 7d7d3754d33b41478fea2d2d7ed902d665a9e03d Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Thu, 21 Mar 2024 02:15:56 +0000 Subject: feature: A better looking bookmark preview page --- .../app/dashboard/preview/[bookmarkId]/page.tsx | 6 +- .../dashboard/bookmarks/BookmarkActionBar.tsx | 9 +- .../dashboard/bookmarks/BookmarkPreview.tsx | 204 +++++++++++++++------ apps/web/components/ui/dialog.tsx | 16 +- apps/web/components/ui/tooltip.tsx | 36 ++++ apps/web/package.json | 2 + 6 files changed, 204 insertions(+), 69 deletions(-) create mode 100644 apps/web/components/ui/tooltip.tsx (limited to 'apps/web') diff --git a/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx b/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx index 5f10b56e..c9f27eac 100644 --- a/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx +++ b/apps/web/app/dashboard/preview/[bookmarkId]/page.tsx @@ -10,5 +10,9 @@ export default async function BookmarkPreviewPage({ bookmarkId: params.bookmarkId, }); - return ; + return ( +
+ +
+ ); } diff --git a/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx index 0d98cc1f..d2beb8d4 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkActionBar.tsx @@ -1,6 +1,5 @@ import { Button } from "@/components/ui/button"; -import { DialogContent } from "@/components/ui/dialog"; -import { Dialog, DialogTrigger } from "@radix-ui/react-dialog"; +import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { Maximize2, Star } from "lucide-react"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; @@ -28,7 +27,11 @@ export default function BookmarkActionBar({ - + e.preventDefault()} + > 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 ( +
+ + + +
); +} - const linkHeader = bookmark.content.type == "link" && ( -
-

- {bookmark.content.title ?? bookmark.content.url} -

- +function CreationTime({ createdAt }: { createdAt: Date }) { + return ( + + + + + {dayjs(createdAt).fromNow()} + + + {createdAt.toLocaleString()} + + + ); +} + +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 ( +
+ + + +

{title}

+
+ + + {title} + + +
+
+ View Original +
); +} +function TextContentSection({ bookmark }: { bookmark: ZBookmark }) { let content; switch (bookmark.content.type) { case "link": { if (!bookmark.content.htmlContent) { content = ( -
Failed to fetch link content ...
+
+ Failed to fetch link content ... +
); } 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 = {bookmark.content.text}; + content = ( + {bookmark.content.text} + ); break; } - case "asset": { + } + + return {content}; +} + +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 = ( -
+
asset
@@ -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 = ; + break; + } + case "asset": { + content = ; + break; + } + } + + const linkHeader = bookmark.content.type == "link" && ( + + ); return ( -
-
- - - -
- - {bookmark.createdAt.toLocaleString()} - -
+
+
+ {isBookmarkStillCrawling(bookmark) ? : content}
-
- {linkHeader} -
- {isBookmarkStillCrawling(bookmark) ? ( -
- - - -
- ) : ( - content - )} +
+ {linkHeader} + +
+ +
); diff --git a/apps/web/components/ui/dialog.tsx b/apps/web/components/ui/dialog.tsx index 8e0c3c6c..18795408 100644 --- a/apps/web/components/ui/dialog.tsx +++ b/apps/web/components/ui/dialog.tsx @@ -30,8 +30,10 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( + React.ComponentPropsWithoutRef & { + hideCloseBtn?: boolean; + } +>(({ className, children, hideCloseBtn = false, ...props }, ref) => ( {children} - - - Close - + {!hideCloseBtn && ( + + + Close + + )} )); diff --git a/apps/web/components/ui/tooltip.tsx b/apps/web/components/ui/tooltip.tsx new file mode 100644 index 00000000..020f6151 --- /dev/null +++ b/apps/web/components/ui/tooltip.tsx @@ -0,0 +1,36 @@ +"use client"; + +import * as React from "react"; +import { cn } from "@/lib/utils"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; +const TooltipPortal = TooltipPrimitive.Portal; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { + Tooltip, + TooltipTrigger, + TooltipContent, + TooltipProvider, + TooltipPortal, +}; diff --git a/apps/web/package.json b/apps/web/package.json index 85cd8942..aa38b882 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -32,6 +32,7 @@ "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-tooltip": "^1.0.7", "@tanstack/react-query": "^5.24.8", "@tanstack/react-query-devtools": "^5.21.0", "@trpc/client": "11.0.0-next-beta.308", @@ -40,6 +41,7 @@ "better-sqlite3": "^9.4.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "dayjs": "^1.11.10", "drizzle-orm": "^0.29.4", "lucide-react": "^0.330.0", "next": "14.1.4", -- cgit v1.2.3-70-g09d2