import * as React from "react"; import { ActionButton } from "@/components/ui/action-button"; import { Button } from "@/components/ui/button"; import { Calendar } from "@/components/ui/calendar"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Textarea } from "@/components/ui/textarea"; import { toast } from "@/components/ui/use-toast"; import { useDialogFormReset } from "@/lib/hooks/useDialogFormReset"; import { useTranslation } from "@/lib/i18n/client"; import { api } from "@/lib/trpc"; import { cn } from "@/lib/utils"; import { zodResolver } from "@hookform/resolvers/zod"; import { format } from "date-fns"; import { CalendarIcon } from "lucide-react"; import { useForm } from "react-hook-form"; import { useUpdateBookmark } from "@karakeep/shared-react/hooks/bookmarks"; import { BookmarkTypes, ZBookmark, ZUpdateBookmarksRequest, zUpdateBookmarksRequestSchema, } from "@karakeep/shared/types/bookmarks"; import { getBookmarkTitle } from "@karakeep/shared/utils/bookmarkUtils"; import { BookmarkTagsEditor } from "./BookmarkTagsEditor"; const formSchema = zUpdateBookmarksRequestSchema; export function EditBookmarkDialog({ open, setOpen, bookmark, children, }: { bookmark: ZBookmark; children?: React.ReactNode; open: boolean; setOpen: (v: boolean) => void; }) { const { t } = useTranslation(); const { data: assetContent, isLoading: isAssetContentLoading } = api.bookmarks.getBookmark.useQuery( { bookmarkId: bookmark.id, includeContent: true, }, { enabled: open && bookmark.content.type == BookmarkTypes.ASSET, select: (b) => b.content.type == BookmarkTypes.ASSET ? b.content.content : null, }, ); const bookmarkToDefault = (bookmark: ZBookmark) => ({ bookmarkId: bookmark.id, summary: bookmark.summary, title: getBookmarkTitle(bookmark), createdAt: bookmark.createdAt ?? new Date(), // Link specific defaults (only if bookmark is a link) url: bookmark.content.type === BookmarkTypes.LINK ? bookmark.content.url : undefined, description: bookmark.content.type === BookmarkTypes.LINK ? (bookmark.content.description ?? "") : undefined, author: bookmark.content.type === BookmarkTypes.LINK ? (bookmark.content.author ?? "") : undefined, publisher: bookmark.content.type === BookmarkTypes.LINK ? (bookmark.content.publisher ?? "") : undefined, datePublished: bookmark.content.type === BookmarkTypes.LINK ? bookmark.content.datePublished : undefined, // Asset specific fields assetContent: assetContent ?? undefined, }); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: bookmarkToDefault(bookmark), }); const { mutate: updateBookmarkMutate, isPending: isUpdatingBookmark } = useUpdateBookmark({ onSuccess: (updatedBookmark) => { toast({ description: "Bookmark details updated successfully!" }); // Close the dialog after successful detail update setOpen(false); // Reset form with potentially updated data form.reset(bookmarkToDefault(updatedBookmark)); }, onError: (error) => { toast({ variant: "destructive", title: "Failed to update bookmark", description: error.message, }); }, }); function onSubmit(values: ZUpdateBookmarksRequest) { // Ensure optional fields that are empty strings are sent as null/undefined if appropriate const payload = { ...values, title: values.title ?? null, }; updateBookmarkMutate(payload); } // Reset form only when dialog is initially opened to preserve unsaved changes // This prevents losing unsaved title edits when tags are updated, which would // cause the bookmark prop to change and trigger a form reset useDialogFormReset(open, form, bookmarkToDefault(bookmark)); // Update assetContent field when it's loaded React.useEffect(() => { if (assetContent && bookmark.content.type === BookmarkTypes.ASSET) { form.setValue("assetContent", assetContent); } }, [assetContent, bookmark.content.type, form]); const isLink = bookmark.content.type === BookmarkTypes.LINK; const isAsset = bookmark.content.type === BookmarkTypes.ASSET; return ( {children && {children}} {t("bookmark_editor.title")} {t("bookmark_editor.subtitle")}
( {t("common.title")} )} /> {isLink && ( ( {t("common.url")} )} /> )} {isLink && ( ( {t("common.description")}