From 49f38efdbe3718055d2c84847d7dab92ae359be9 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Tue, 1 Jul 2025 20:26:25 +0000 Subject: feat: Add a proper reader mode --- .../components/dashboard/lists/ShareListModal.tsx | 2 +- .../dashboard/preview/BookmarkHtmlHighlighter.tsx | 3 + .../dashboard/preview/LinkContentSection.tsx | 237 +++++++-------------- .../components/dashboard/preview/ReaderView.tsx | 121 +++++++++++ .../dashboard/preview/TextContentSection.tsx | 2 +- 5 files changed, 207 insertions(+), 158 deletions(-) create mode 100644 apps/web/components/dashboard/preview/ReaderView.tsx (limited to 'apps/web/components/dashboard') diff --git a/apps/web/components/dashboard/lists/ShareListModal.tsx b/apps/web/components/dashboard/lists/ShareListModal.tsx index 16668e67..4b8218a9 100644 --- a/apps/web/components/dashboard/lists/ShareListModal.tsx +++ b/apps/web/components/dashboard/lists/ShareListModal.tsx @@ -4,13 +4,13 @@ import { Dialog, DialogClose, DialogContent, + DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { useTranslation } from "@/lib/i18n/client"; -import { DialogDescription } from "@radix-ui/react-dialog"; import { ZBookmarkList } from "@karakeep/shared/types/lists"; 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} /> - 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 = ; - } else if (!cachedContent) { - content = ( -
Failed to fetch link content ...
- ); - } else { - content = ( - - 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}; -} - function VideoSection({ link }: { link: ZBookmarkedLink }) { return (
@@ -182,7 +82,14 @@ export default function LinkContentSection({ let content; if (section === "cached") { - content = ; + content = ( + + + + ); } else if (section === "archive") { content = ; } else if (section === "video") { @@ -193,50 +100,68 @@ export default function LinkContentSection({ return (
- +
+ + {section === "cached" && ( + + + + + + + FullScreen + + )} +
{content}
); 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 = ; + } else if (!cachedContent) { + content = ( +
Failed to fetch link content ...
+ ); + } else { + content = ( + + 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"; -- cgit v1.2.3-70-g09d2