diff options
Diffstat (limited to 'apps/web/components/dashboard/preview')
7 files changed, 82 insertions, 38 deletions
diff --git a/apps/web/components/dashboard/preview/AttachmentBox.tsx b/apps/web/components/dashboard/preview/AttachmentBox.tsx index e24cc646..73eea640 100644 --- a/apps/web/components/dashboard/preview/AttachmentBox.tsx +++ b/apps/web/components/dashboard/preview/AttachmentBox.tsx @@ -27,7 +27,13 @@ import { isAllowedToDetachAsset, } from "@karakeep/trpc/lib/attachments"; -export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { +export default function AttachmentBox({ + bookmark, + readOnly = false, +}: { + bookmark: ZBookmark; + readOnly?: boolean; +}) { const { t } = useTranslation(); const { mutate: attachAsset, isPending: isAttaching } = useAttachBookmarkAsset({ @@ -122,7 +128,8 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { > <Download className="size-4" /> </Link> - {isAllowedToAttachAsset(asset.assetType) && + {!readOnly && + isAllowedToAttachAsset(asset.assetType) && asset.assetType !== "userUploaded" && ( <FilePickerButton title="Replace" @@ -147,7 +154,7 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { <Pencil className="size-4" /> </FilePickerButton> )} - {isAllowedToDetachAsset(asset.assetType) && ( + {!readOnly && isAllowedToDetachAsset(asset.assetType) && ( <ActionConfirmingDialog title="Delete Attachment?" description={`Are you sure you want to delete the attachment of the bookmark?`} @@ -175,7 +182,8 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { </div> </div> ))} - {!bookmark.assets.some((asset) => asset.assetType == "bannerImage") && + {!readOnly && + !bookmark.assets.some((asset) => asset.assetType == "bannerImage") && bookmark.content.type != BookmarkTypes.ASSET && ( <FilePickerButton title="Attach a Banner" @@ -203,30 +211,32 @@ export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { Attach a Banner </FilePickerButton> )} - <FilePickerButton - title="Upload File" - loading={isAttaching} - multiple={false} - variant="ghost" - size="none" - className="flex w-full items-center justify-center gap-2" - onFileSelect={(file) => - uploadAsset(file, { - onSuccess: (resp) => { - attachAsset({ - bookmarkId: bookmark.id, - asset: { - id: resp.assetId, - assetType: "userUploaded", - }, - }); - }, - }) - } - > - <Plus className="size-4" /> - Upload File - </FilePickerButton> + {!readOnly && ( + <FilePickerButton + title="Upload File" + loading={isAttaching} + multiple={false} + variant="ghost" + size="none" + className="flex w-full items-center justify-center gap-2" + onFileSelect={(file) => + uploadAsset(file, { + onSuccess: (resp) => { + attachAsset({ + bookmarkId: bookmark.id, + asset: { + id: resp.assetId, + assetType: "userUploaded", + }, + }); + }, + }) + } + > + <Plus className="size-4" /> + Upload File + </FilePickerButton> + )} </CollapsibleContent> </Collapsible> ); diff --git a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx index 19499d3e..e0f20ea2 100644 --- a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx +++ b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx @@ -95,6 +95,7 @@ interface HTMLHighlighterProps { style?: React.CSSProperties; className?: string; highlights?: Highlight[]; + readOnly?: boolean; onHighlight?: (highlight: Highlight) => void; onUpdateHighlight?: (highlight: Highlight) => void; onDeleteHighlight?: (highlight: Highlight) => void; @@ -105,6 +106,7 @@ function BookmarkHTMLHighlighter({ className, style, highlights = [], + readOnly = false, onHighlight, onUpdateHighlight, onDeleteHighlight, @@ -173,6 +175,10 @@ function BookmarkHTMLHighlighter({ }, [pendingHighlight, contentRef]); const handlePointerUp = (e: React.PointerEvent) => { + if (readOnly) { + return; + } + const selection = window.getSelection(); // Check if we clicked on an existing highlight diff --git a/apps/web/components/dashboard/preview/BookmarkPreview.tsx b/apps/web/components/dashboard/preview/BookmarkPreview.tsx index 4766bd32..7e6bf814 100644 --- a/apps/web/components/dashboard/preview/BookmarkPreview.tsx +++ b/apps/web/components/dashboard/preview/BookmarkPreview.tsx @@ -17,6 +17,7 @@ import useRelativeTime from "@/lib/hooks/relative-time"; import { useTranslation } from "@/lib/i18n/client"; import { api } from "@/lib/trpc"; import { Building, CalendarDays, ExternalLink, User } from "lucide-react"; +import { useSession } from "next-auth/react"; import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks"; import { @@ -117,6 +118,7 @@ export default function BookmarkPreview({ }) { const { t } = useTranslation(); const [activeTab, setActiveTab] = useState<string>("content"); + const { data: session } = useSession(); const { data: bookmark } = api.bookmarks.getBookmark.useQuery( { @@ -138,6 +140,9 @@ export default function BookmarkPreview({ return <FullPageSpinner />; } + // Check if the current user owns this bookmark + const isOwner = session?.user?.id === bookmark.userId; + let content; switch (bookmark.content.type) { case BookmarkTypes.LINK: { @@ -186,18 +191,18 @@ export default function BookmarkPreview({ </div> <CreationTime createdAt={bookmark.createdAt} /> <BookmarkMetadata bookmark={bookmark} /> - <SummarizeBookmarkArea bookmark={bookmark} /> + <SummarizeBookmarkArea bookmark={bookmark} readOnly={!isOwner} /> <div className="flex items-center gap-4"> <p className="text-sm text-gray-400">{t("common.tags")}</p> - <BookmarkTagsEditor bookmark={bookmark} /> + <BookmarkTagsEditor bookmark={bookmark} disabled={!isOwner} /> </div> <div className="flex gap-4"> <p className="pt-2 text-sm text-gray-400">{t("common.note")}</p> - <NoteEditor bookmark={bookmark} /> + <NoteEditor bookmark={bookmark} disabled={!isOwner} /> </div> - <AttachmentBox bookmark={bookmark} /> - <HighlightsBox bookmarkId={bookmark.id} /> - <ActionBar bookmark={bookmark} /> + <AttachmentBox bookmark={bookmark} readOnly={!isOwner} /> + <HighlightsBox bookmarkId={bookmark.id} readOnly={!isOwner} /> + {isOwner && <ActionBar bookmark={bookmark} />} </div> ); diff --git a/apps/web/components/dashboard/preview/HighlightsBox.tsx b/apps/web/components/dashboard/preview/HighlightsBox.tsx index 4da22d04..41ab7d74 100644 --- a/apps/web/components/dashboard/preview/HighlightsBox.tsx +++ b/apps/web/components/dashboard/preview/HighlightsBox.tsx @@ -11,7 +11,13 @@ import { ChevronsDownUp } from "lucide-react"; import HighlightCard from "../highlights/HighlightCard"; -export default function HighlightsBox({ bookmarkId }: { bookmarkId: string }) { +export default function HighlightsBox({ + bookmarkId, + readOnly, +}: { + bookmarkId: string; + readOnly: boolean; +}) { const { t } = useTranslation(); const { data: highlights, isPending: isLoading } = @@ -30,7 +36,11 @@ export default function HighlightsBox({ bookmarkId }: { bookmarkId: string }) { <CollapsibleContent className="group flex flex-col py-3 text-sm"> {highlights.highlights.map((highlight) => ( <Fragment key={highlight.id}> - <HighlightCard highlight={highlight} clickable /> + <HighlightCard + highlight={highlight} + clickable + readOnly={readOnly} + /> <Separator className="m-2 h-0.5 bg-gray-200 last:hidden" /> </Fragment> ))} diff --git a/apps/web/components/dashboard/preview/LinkContentSection.tsx b/apps/web/components/dashboard/preview/LinkContentSection.tsx index 53559aa0..64b62df6 100644 --- a/apps/web/components/dashboard/preview/LinkContentSection.tsx +++ b/apps/web/components/dashboard/preview/LinkContentSection.tsx @@ -25,6 +25,7 @@ import { ExpandIcon, Video, } from "lucide-react"; +import { useSession } from "next-auth/react"; import { useQueryState } from "nuqs"; import { ErrorBoundary } from "react-error-boundary"; @@ -111,6 +112,8 @@ export default function LinkContentSection({ const [section, setSection] = useQueryState("section", { defaultValue: defaultSection, }); + const { data: session } = useSession(); + const isOwner = session?.user?.id === bookmark.userId; if (bookmark.content.type != BookmarkTypes.LINK) { throw new Error("Invalid content type"); @@ -133,6 +136,7 @@ export default function LinkContentSection({ <ReaderView className="prose mx-auto dark:prose-invert" bookmarkId={bookmark.id} + readOnly={!isOwner} /> </ScrollArea> ); diff --git a/apps/web/components/dashboard/preview/NoteEditor.tsx b/apps/web/components/dashboard/preview/NoteEditor.tsx index 393628b5..538aff2e 100644 --- a/apps/web/components/dashboard/preview/NoteEditor.tsx +++ b/apps/web/components/dashboard/preview/NoteEditor.tsx @@ -5,7 +5,13 @@ import { useClientConfig } from "@/lib/clientConfig"; import type { ZBookmark } from "@karakeep/shared/types/bookmarks"; import { useUpdateBookmark } from "@karakeep/shared-react/hooks/bookmarks"; -export function NoteEditor({ bookmark }: { bookmark: ZBookmark }) { +export function NoteEditor({ + bookmark, + disabled, +}: { + bookmark: ZBookmark; + disabled?: boolean; +}) { const demoMode = !!useClientConfig().demoMode; const updateBookmarkMutator = useUpdateBookmark({ @@ -26,7 +32,7 @@ export function NoteEditor({ bookmark }: { bookmark: ZBookmark }) { <Textarea className="h-44 w-full overflow-auto rounded bg-background p-2 text-sm text-gray-400 dark:text-gray-300" defaultValue={bookmark.note ?? ""} - disabled={demoMode} + disabled={demoMode || disabled} placeholder="Write some notes ..." onBlur={(e) => { if (e.currentTarget.value == bookmark.note) { diff --git a/apps/web/components/dashboard/preview/ReaderView.tsx b/apps/web/components/dashboard/preview/ReaderView.tsx index bf4c27a5..1974626a 100644 --- a/apps/web/components/dashboard/preview/ReaderView.tsx +++ b/apps/web/components/dashboard/preview/ReaderView.tsx @@ -15,10 +15,12 @@ export default function ReaderView({ bookmarkId, className, style, + readOnly, }: { bookmarkId: string; className?: string; style?: React.CSSProperties; + readOnly: boolean; }) { const { data: highlights } = api.highlights.getForBookmark.useQuery({ bookmarkId, @@ -93,6 +95,7 @@ export default function ReaderView({ style={style} htmlContent={cachedContent || ""} highlights={highlights?.highlights ?? []} + readOnly={readOnly} onDeleteHighlight={(h) => deleteHighlight({ highlightId: h.id, |
