import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useAddFileMutation, useFileQuery, useUpdateFileMutation, } from "../../../hooks/useFile"; import { useSodium } from "../../../hooks/useSodium"; import { PUBLIC_KEY } from "../../../consts"; import { useNotyf } from "../../../hooks/useNotyf"; const schema = z.object({ path: z.string().min(1, "Path is required"), content: z.string().min(1, "Content is required"), }); type FormValues = z.infer; export type AddFileModalProps = { isOpen: boolean; onClose: () => void; sandboxId: string; fileId?: string; }; function AddFileModal({ isOpen, onClose, sandboxId, fileId, }: AddFileModalProps) { const sodium = useSodium(); const notyf = useNotyf(); const [isLoading, setIsLoading] = useState(false); const { mutateAsync: addFile } = useAddFileMutation(); const { data } = useFileQuery(fileId!); const { mutateAsync: updateFile } = useUpdateFileMutation(); const { register, handleSubmit, reset, formState: { errors }, } = useForm({ resolver: zodResolver(schema), }); useEffect(() => { if (data) { reset({ path: data.file.path }); } }, [data, reset]); useEffect(() => { const handleEscapeKey = (event: KeyboardEvent) => { if (event.key === "Escape" && isOpen) { reset(); onClose(); } }; document.addEventListener("keydown", handleEscapeKey); return () => { document.removeEventListener("keydown", handleEscapeKey); }; }, [isOpen, onClose, reset]); const handleBackdropClick = (e: React.MouseEvent) => { e.stopPropagation(); if (e.target === e.currentTarget) { reset(); onClose(); } }; const handleContentClick = (e: React.MouseEvent) => { e.stopPropagation(); }; const handleCloseButton = (e: React.MouseEvent) => { reset(); e.stopPropagation(); onClose(); }; const onSubmit = async (data: FormValues) => { setIsLoading(true); const sealed = sodium.cryptoBoxSeal( sodium.fromString(data.content), sodium.fromHex(PUBLIC_KEY), ); try { if (fileId) { await updateFile({ id: fileId, path: data.path, content: sodium.toBase64( sealed, sodium.base64Variants.URLSAFE_NO_PADDING, ), }); setIsLoading(false); reset(); onClose(); notyf.open("primary", "File updated successfully!"); return; } await addFile({ sandboxId, path: data.path, content: sodium.toBase64( sealed, sodium.base64Variants.URLSAFE_NO_PADDING, ), }); setIsLoading(false); reset(); onClose(); notyf.open("primary", "File added successfully!"); } catch { notyf.open("error", "Failed to add file!"); setIsLoading(false); reset(); onClose(); return; } }; if (!isOpen) return null; return createPortal( <>
{fileId ? "Edit File" : "Add File"}
{errors.path && ( {errors.path.message} )}
{errors.content && ( {errors.content.message} )}
e.stopPropagation()} >
, document.body, ); } export default AddFileModal;