diff options
Diffstat (limited to 'apps/web/components/dashboard/preview')
4 files changed, 206 insertions, 157 deletions
diff --git a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx index dc446112..19499d3e 100644 --- a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx +++ b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx @@ -92,6 +92,7 @@ export interface Highlight { interface HTMLHighlighterProps { htmlContent: string; + style?: React.CSSProperties; className?: string; highlights?: Highlight[]; onHighlight?: (highlight: Highlight) => void; @@ -102,6 +103,7 @@ interface HTMLHighlighterProps { function BookmarkHTMLHighlighter({ htmlContent, className, + style, highlights = [], onHighlight, onUpdateHighlight, @@ -345,6 +347,7 @@ function BookmarkHTMLHighlighter({ dangerouslySetInnerHTML={{ __html: htmlContent }} onPointerUp={handlePointerUp} className={className} + style={style} /> <ColorPickerMenu position={menuPosition} diff --git a/apps/web/components/dashboard/preview/LinkContentSection.tsx b/apps/web/components/dashboard/preview/LinkContentSection.tsx index e86be6b7..eefec701 100644 --- a/apps/web/components/dashboard/preview/LinkContentSection.tsx +++ b/apps/web/components/dashboard/preview/LinkContentSection.tsx @@ -1,7 +1,8 @@ import { useState } from "react"; import Image from "next/image"; -import BookmarkHTMLHighlighter from "@/components/dashboard/preview/BookmarkHtmlHighlighter"; -import { FullPageSpinner } from "@/components/ui/full-page-spinner"; +import Link from "next/link"; +import { buttonVariants } from "@/components/ui/button"; +import { ScrollArea } from "@/components/ui/scroll-area"; import { Select, SelectContent, @@ -10,23 +11,22 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { toast } from "@/components/ui/use-toast"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { useTranslation } from "@/lib/i18n/client"; -import { api } from "@/lib/trpc"; -import { ScrollArea } from "@radix-ui/react-scroll-area"; -import { Archive, BookOpen, Camera, Video } from "lucide-react"; +import { Archive, BookOpen, Camera, ExpandIcon, Video } from "lucide-react"; import { - useCreateHighlight, - useDeleteHighlight, - useUpdateHighlight, -} from "@karakeep/shared-react/hooks/highlights"; -import { BookmarkTypes, ZBookmark, ZBookmarkedLink, } from "@karakeep/shared/types/bookmarks"; +import ReaderView from "./ReaderView"; + function FullPageArchiveSection({ link }: { link: ZBookmarkedLink }) { const archiveAssetId = link.fullPageArchiveAssetId ?? link.precrawledArchiveAssetId; @@ -54,106 +54,6 @@ function ScreenshotSection({ link }: { link: ZBookmarkedLink }) { ); } -function CachedContentSection({ bookmarkId }: { bookmarkId: string }) { - const { data: highlights } = api.highlights.getForBookmark.useQuery({ - bookmarkId, - }); - const { data: cachedContent, isPending: isCachedContentLoading } = - api.bookmarks.getBookmark.useQuery( - { - bookmarkId, - includeContent: true, - }, - { - select: (data) => - data.content.type == BookmarkTypes.LINK - ? data.content.htmlContent - : null, - }, - ); - - const { mutate: createHighlight } = useCreateHighlight({ - onSuccess: () => { - toast({ - description: "Highlight has been created!", - }); - }, - onError: () => { - toast({ - variant: "destructive", - description: "Something went wrong", - }); - }, - }); - - const { mutate: updateHighlight } = useUpdateHighlight({ - onSuccess: () => { - toast({ - description: "Highlight has been updated!", - }); - }, - onError: () => { - toast({ - variant: "destructive", - description: "Something went wrong", - }); - }, - }); - - const { mutate: deleteHighlight } = useDeleteHighlight({ - onSuccess: () => { - toast({ - description: "Highlight has been deleted!", - }); - }, - onError: () => { - toast({ - variant: "destructive", - description: "Something went wrong", - }); - }, - }); - - let content; - if (isCachedContentLoading) { - content = <FullPageSpinner />; - } else if (!cachedContent) { - content = ( - <div className="text-destructive">Failed to fetch link content ...</div> - ); - } else { - content = ( - <BookmarkHTMLHighlighter - htmlContent={cachedContent || ""} - className="prose mx-auto dark:prose-invert" - highlights={highlights?.highlights ?? []} - onDeleteHighlight={(h) => - deleteHighlight({ - highlightId: h.id, - }) - } - onUpdateHighlight={(h) => - updateHighlight({ - highlightId: h.id, - color: h.color, - }) - } - onHighlight={(h) => - createHighlight({ - startOffset: h.startOffset, - endOffset: h.endOffset, - color: h.color, - bookmarkId, - text: h.text, - note: null, - }) - } - /> - ); - } - return <ScrollArea className="h-full">{content}</ScrollArea>; -} - function VideoSection({ link }: { link: ZBookmarkedLink }) { return ( <div className="relative h-full w-full overflow-hidden"> @@ -182,7 +82,14 @@ export default function LinkContentSection({ let content; if (section === "cached") { - content = <CachedContentSection bookmarkId={bookmark.id} />; + content = ( + <ScrollArea className="h-full"> + <ReaderView + className="prose mx-auto dark:prose-invert" + bookmarkId={bookmark.id} + /> + </ScrollArea> + ); } else if (section === "archive") { content = <FullPageArchiveSection link={bookmark.content} />; } else if (section === "video") { @@ -193,50 +100,68 @@ export default function LinkContentSection({ return ( <div className="flex h-full flex-col items-center gap-2"> - <Select onValueChange={setSection} value={section}> - <SelectTrigger className="w-fit"> - <span className="mr-2"> - <SelectValue /> - </span> - </SelectTrigger> - <SelectContent> - <SelectGroup> - <SelectItem value="cached"> - <div className="flex items-center"> - <BookOpen className="mr-2 h-4 w-4" /> - {t("preview.reader_view")} - </div> - </SelectItem> - <SelectItem - value="screenshot" - disabled={!bookmark.content.screenshotAssetId} - > - <div className="flex items-center"> - <Camera className="mr-2 h-4 w-4" /> - {t("common.screenshot")} - </div> - </SelectItem> - <SelectItem - value="archive" - disabled={ - !bookmark.content.fullPageArchiveAssetId && - !bookmark.content.precrawledArchiveAssetId - } - > - <div className="flex items-center"> - <Archive className="mr-2 h-4 w-4" /> - {t("common.archive")} - </div> - </SelectItem> - <SelectItem value="video" disabled={!bookmark.content.videoAssetId}> - <div className="flex items-center"> - <Video className="mr-2 h-4 w-4" /> - {t("common.video")} - </div> - </SelectItem> - </SelectGroup> - </SelectContent> - </Select> + <div className="flex items-center gap-2"> + <Select onValueChange={setSection} value={section}> + <SelectTrigger className="w-fit"> + <span className="mr-2"> + <SelectValue /> + </span> + </SelectTrigger> + <SelectContent> + <SelectGroup> + <SelectItem value="cached"> + <div className="flex items-center"> + <BookOpen className="mr-2 h-4 w-4" /> + {t("preview.reader_view")} + </div> + </SelectItem> + <SelectItem + value="screenshot" + disabled={!bookmark.content.screenshotAssetId} + > + <div className="flex items-center"> + <Camera className="mr-2 h-4 w-4" /> + {t("common.screenshot")} + </div> + </SelectItem> + <SelectItem + value="archive" + disabled={ + !bookmark.content.fullPageArchiveAssetId && + !bookmark.content.precrawledArchiveAssetId + } + > + <div className="flex items-center"> + <Archive className="mr-2 h-4 w-4" /> + {t("common.archive")} + </div> + </SelectItem> + <SelectItem + value="video" + disabled={!bookmark.content.videoAssetId} + > + <div className="flex items-center"> + <Video className="mr-2 h-4 w-4" /> + {t("common.video")} + </div> + </SelectItem> + </SelectGroup> + </SelectContent> + </Select> + {section === "cached" && ( + <Tooltip> + <TooltipTrigger> + <Link + href={`/reader/${bookmark.id}`} + className={buttonVariants({ variant: "outline" })} + > + <ExpandIcon className="h-4 w-4" /> + </Link> + </TooltipTrigger> + <TooltipContent side="bottom">FullScreen</TooltipContent> + </Tooltip> + )} + </div> {content} </div> ); diff --git a/apps/web/components/dashboard/preview/ReaderView.tsx b/apps/web/components/dashboard/preview/ReaderView.tsx new file mode 100644 index 00000000..bf4c27a5 --- /dev/null +++ b/apps/web/components/dashboard/preview/ReaderView.tsx @@ -0,0 +1,121 @@ +import { FullPageSpinner } from "@/components/ui/full-page-spinner"; +import { toast } from "@/components/ui/use-toast"; +import { api } from "@/lib/trpc"; + +import { + useCreateHighlight, + useDeleteHighlight, + useUpdateHighlight, +} from "@karakeep/shared-react/hooks/highlights"; +import { BookmarkTypes } from "@karakeep/shared/types/bookmarks"; + +import BookmarkHTMLHighlighter from "./BookmarkHtmlHighlighter"; + +export default function ReaderView({ + bookmarkId, + className, + style, +}: { + bookmarkId: string; + className?: string; + style?: React.CSSProperties; +}) { + const { data: highlights } = api.highlights.getForBookmark.useQuery({ + bookmarkId, + }); + const { data: cachedContent, isPending: isCachedContentLoading } = + api.bookmarks.getBookmark.useQuery( + { + bookmarkId, + includeContent: true, + }, + { + select: (data) => + data.content.type == BookmarkTypes.LINK + ? data.content.htmlContent + : null, + }, + ); + + const { mutate: createHighlight } = useCreateHighlight({ + onSuccess: () => { + toast({ + description: "Highlight has been created!", + }); + }, + onError: () => { + toast({ + variant: "destructive", + description: "Something went wrong", + }); + }, + }); + + const { mutate: updateHighlight } = useUpdateHighlight({ + onSuccess: () => { + toast({ + description: "Highlight has been updated!", + }); + }, + onError: () => { + toast({ + variant: "destructive", + description: "Something went wrong", + }); + }, + }); + + const { mutate: deleteHighlight } = useDeleteHighlight({ + onSuccess: () => { + toast({ + description: "Highlight has been deleted!", + }); + }, + onError: () => { + toast({ + variant: "destructive", + description: "Something went wrong", + }); + }, + }); + + let content; + if (isCachedContentLoading) { + content = <FullPageSpinner />; + } else if (!cachedContent) { + content = ( + <div className="text-destructive">Failed to fetch link content ...</div> + ); + } else { + content = ( + <BookmarkHTMLHighlighter + className={className} + style={style} + htmlContent={cachedContent || ""} + highlights={highlights?.highlights ?? []} + onDeleteHighlight={(h) => + deleteHighlight({ + highlightId: h.id, + }) + } + onUpdateHighlight={(h) => + updateHighlight({ + highlightId: h.id, + color: h.color, + }) + } + onHighlight={(h) => + createHighlight({ + startOffset: h.startOffset, + endOffset: h.endOffset, + color: h.color, + bookmarkId, + text: h.text, + note: null, + }) + } + /> + ); + } + return content; +} diff --git a/apps/web/components/dashboard/preview/TextContentSection.tsx b/apps/web/components/dashboard/preview/TextContentSection.tsx index 4e33bb92..a4510698 100644 --- a/apps/web/components/dashboard/preview/TextContentSection.tsx +++ b/apps/web/components/dashboard/preview/TextContentSection.tsx @@ -1,6 +1,6 @@ import Image from "next/image"; import { BookmarkMarkdownComponent } from "@/components/dashboard/bookmarks/BookmarkMarkdownComponent"; -import { ScrollArea } from "@radix-ui/react-scroll-area"; +import { ScrollArea } from "@/components/ui/scroll-area"; import type { ZBookmarkTypeText } from "@karakeep/shared/types/bookmarks"; import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks"; |
