"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 { TagsEditor } from "./TagsEditor"; dayjs.extend(relativeTime); function ContentLoading() { return (
); } 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 ...
); } else { content = (
); } break; } case "text": { content = ( {bookmark.content.text} ); break; } } 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
); } } 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 (
{isBookmarkStillCrawling(bookmark) ? : content}
{linkHeader}
); }