aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/preview/ReaderView.tsx
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-07-01 20:26:25 +0000
committerMohamed Bassem <me@mbassem.com>2025-07-19 11:45:30 +0000
commit49f38efdbe3718055d2c84847d7dab92ae359be9 (patch)
tree17d4b6d062ce885fd2de3a05ced224ab49292586 /apps/web/components/dashboard/preview/ReaderView.tsx
parent4a4ff37b6283df1d6327a3f8ddff8b74f989ec36 (diff)
downloadkarakeep-49f38efdbe3718055d2c84847d7dab92ae359be9.tar.zst
feat: Add a proper reader mode
Diffstat (limited to 'apps/web/components/dashboard/preview/ReaderView.tsx')
-rw-r--r--apps/web/components/dashboard/preview/ReaderView.tsx121
1 files changed, 121 insertions, 0 deletions
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;
+}