diff options
| author | MohamedBassem <me@mbassem.com> | 2024-04-15 18:39:59 +0100 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-04-15 18:55:34 +0100 |
| commit | 81e0b2849d837649da9adbc5d077b8c819fe7bee (patch) | |
| tree | 003bb21413372825dc19c07a87bdbe6692e384a9 /apps/web/components/dashboard/preview | |
| parent | 5c9acb1cb3bfe341378b91bbed344dd7202a00d7 (diff) | |
| download | karakeep-81e0b2849d837649da9adbc5d077b8c819fe7bee.tar.zst | |
feature: Add title to bookmarks and allow editing them. Fixes #27
Diffstat (limited to 'apps/web/components/dashboard/preview')
| -rw-r--r-- | apps/web/components/dashboard/preview/BookmarkPreview.tsx | 51 | ||||
| -rw-r--r-- | apps/web/components/dashboard/preview/EditableTitle.tsx | 165 |
2 files changed, 180 insertions, 36 deletions
diff --git a/apps/web/components/dashboard/preview/BookmarkPreview.tsx b/apps/web/components/dashboard/preview/BookmarkPreview.tsx index 73e49376..93f14c64 100644 --- a/apps/web/components/dashboard/preview/BookmarkPreview.tsx +++ b/apps/web/components/dashboard/preview/BookmarkPreview.tsx @@ -24,6 +24,7 @@ import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; import ActionBar from "./ActionBar"; import { AssetContentSection } from "./AssetContentSection"; +import { EditableTitle } from "./EditableTitle"; import { NoteEditor } from "./NoteEditor"; import { TextContentSection } from "./TextContentSection"; @@ -62,37 +63,6 @@ function CreationTime({ createdAt }: { createdAt: Date }) { ); } -function LinkHeader({ bookmark }: { bookmark: ZBookmark }) { - if (bookmark.content.type !== "link") { - throw new Error("Unexpected content type"); - } - - const title = bookmark.content.title ?? bookmark.content.url; - - return ( - <div className="flex w-full flex-col items-center justify-center space-y-3"> - <Tooltip> - <TooltipTrigger asChild> - <p className="line-clamp-2 text-center text-lg">{title}</p> - </TooltipTrigger> - <TooltipPortal> - <TooltipContent side="bottom" className="w-96"> - {title} - </TooltipContent> - </TooltipPortal> - </Tooltip> - <Link - href={bookmark.content.url} - className="mx-auto flex gap-2 text-gray-400" - > - <span className="my-auto">View Original</span> - <ExternalLink /> - </Link> - <Separator /> - </div> - ); -} - export default function BookmarkPreview({ initialData, }: { @@ -131,17 +101,26 @@ export default function BookmarkPreview({ } } - const linkHeader = bookmark.content.type == "link" && ( - <LinkHeader bookmark={bookmark} /> - ); - return ( <div className="grid h-full grid-rows-3 gap-2 overflow-hidden bg-background lg:grid-cols-3 lg:grid-rows-none"> <div className="row-span-2 h-full w-full overflow-auto p-2 md:col-span-2 lg:row-auto"> {isBookmarkStillCrawling(bookmark) ? <ContentLoading /> : content} </div> <div className="lg:col-span1 row-span-1 flex flex-col gap-4 overflow-auto bg-accent p-4 lg:row-auto"> - {linkHeader} + <div className="flex w-full flex-col items-center justify-center gap-y-2"> + <EditableTitle bookmark={bookmark} /> + {bookmark.content.type == "link" && ( + <Link + href={bookmark.content.url} + className="flex items-center gap-2 text-gray-400" + > + <span>View Original</span> + <ExternalLink /> + </Link> + )} + <Separator /> + </div> + <CreationTime createdAt={bookmark.createdAt} /> <div className="flex gap-4"> <p className="text-sm text-gray-400">Tags</p> diff --git a/apps/web/components/dashboard/preview/EditableTitle.tsx b/apps/web/components/dashboard/preview/EditableTitle.tsx new file mode 100644 index 00000000..1500212d --- /dev/null +++ b/apps/web/components/dashboard/preview/EditableTitle.tsx @@ -0,0 +1,165 @@ +import { useEffect, useRef, useState } from "react"; +import { ActionButtonWithTooltip } from "@/components/ui/action-button"; +import { ButtonWithTooltip } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipPortal, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { toast } from "@/components/ui/use-toast"; +import { Check, Pencil, X } from "lucide-react"; + +import { useUpdateBookmark } from "@hoarder/shared-react/hooks/bookmarks"; +import { ZBookmark } from "@hoarder/trpc/types/bookmarks"; + +interface Props { + bookmarkId: string; + originalTitle: string | null; + setEditable: (editable: boolean) => void; +} + +function EditMode({ bookmarkId, originalTitle, setEditable }: Props) { + const ref = useRef<HTMLDivElement>(null); + + const { mutate: updateBookmark, isPending } = useUpdateBookmark({ + onSuccess: () => { + toast({ + description: "Title updated!", + }); + }, + }); + + useEffect(() => { + if (ref.current) { + ref.current.focus(); + ref.current.textContent = originalTitle; + } + }, [ref]); + + const onSave = () => { + let toSave: string | null = ref.current?.textContent ?? null; + if (originalTitle == toSave) { + // Nothing to do here + return; + } + if (toSave == "") { + toSave = null; + } + updateBookmark({ + bookmarkId, + title: toSave, + }); + setEditable(false); + }; + + return ( + <div className="flex gap-3"> + <div + ref={ref} + role="presentation" + className="p-2 text-center text-lg" + contentEditable={true} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + } + }} + /> + <ActionButtonWithTooltip + tooltip="Save" + delayDuration={500} + size="none" + variant="ghost" + className="align-middle text-gray-400" + loading={isPending} + onClick={() => onSave()} + > + <Check className="size-4" /> + </ActionButtonWithTooltip> + <ButtonWithTooltip + tooltip="Cancel" + delayDuration={500} + size="none" + variant="ghost" + className="align-middle text-gray-400" + onClick={() => { + setEditable(false); + }} + > + <X className="size-4" /> + </ButtonWithTooltip> + </div> + ); +} + +function ViewMode({ originalTitle, setEditable }: Props) { + return ( + <Tooltip delayDuration={500}> + <div className="flex items-center gap-3 text-center"> + <TooltipTrigger asChild> + {originalTitle ? ( + <p className="line-clamp-2 text-lg">{originalTitle}</p> + ) : ( + <p className="text-lg italic text-gray-600">Untitled</p> + )} + </TooltipTrigger> + <ButtonWithTooltip + delayDuration={500} + tooltip="Edit title" + size="none" + variant="ghost" + className="align-middle text-gray-400" + onClick={() => { + setEditable(true); + }} + > + <Pencil className="size-4" /> + </ButtonWithTooltip> + </div> + <TooltipPortal> + {originalTitle && ( + <TooltipContent side="bottom" className="max-w-[40ch]"> + {originalTitle} + </TooltipContent> + )} + </TooltipPortal> + </Tooltip> + ); +} + +export function EditableTitle({ bookmark }: { bookmark: ZBookmark }) { + const [editable, setEditable] = useState(false); + + let title: string | null = null; + switch (bookmark.content.type) { + case "link": + title = bookmark.content.title ?? bookmark.content.url; + break; + case "text": + title = null; + break; + case "asset": + title = bookmark.content.fileName ?? null; + break; + } + + title = bookmark.title ?? title; + if (title == "") { + title = null; + } + + return editable ? ( + <EditMode + bookmarkId={bookmark.id} + originalTitle={title} + setEditable={setEditable} + /> + ) : ( + <ViewMode + bookmarkId={bookmark.id} + originalTitle={title} + setEditable={setEditable} + /> + ); +} |
