aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/preview
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/components/dashboard/preview')
-rw-r--r--apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx3
-rw-r--r--apps/web/components/dashboard/preview/LinkContentSection.tsx237
-rw-r--r--apps/web/components/dashboard/preview/ReaderView.tsx121
-rw-r--r--apps/web/components/dashboard/preview/TextContentSection.tsx2
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";