import { Play, X } from "phosphor-react"; import { HTMLInputTypeAttribute, useCallback, useEffect, useState, } from "react"; import state, { IAccount, IFile, ILog } from "../../state"; import Button from "../Button"; import Box from "../Box"; import Input, { Label } from "../Input"; import Stack from "../Stack"; import { Dialog, DialogTrigger, DialogContent, DialogTitle, DialogDescription, DialogClose, } from "../Dialog"; import Flex from "../Flex"; import { useSnapshot } from "valtio"; import Select from "../Select"; import Text from "../Text"; import { saveFile } from "../../state/actions/saveFile"; import { getErrors, getTags } from "../../utils/comment-parser"; import toast from "react-hot-toast"; const generateHtmlTemplate = (code: string, data?: Record) => { let processString: string | undefined; const process = { env: { NODE_ENV: "production" } } as any; if (data) { Object.keys(data).forEach(key => { process.env[key] = data[key]; }); } processString = JSON.stringify(process); return ` `; }; type Fields = Record< string, { name: string; value: string; type?: "Account" | `Account.${keyof IAccount}` | HTMLInputTypeAttribute; description?: string; required?: boolean; } >; const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => { const snap = useSnapshot(state); const [templateError, setTemplateError] = useState(""); const [fields, setFields] = useState({}); const [iFrameCode, setIframeCode] = useState(""); const [isDialogOpen, setIsDialogOpen] = useState(false); const getFields = useCallback(() => { const inputTags = ["input", "param", "arg", "argument"]; const tags = getTags(content) .filter(tag => inputTags.includes(tag.tag)) .filter(tag => !!tag.name); let _fields = tags.map(tag => ({ name: tag.name, value: tag.default || "", type: tag.type, description: tag.description, required: !tag.optional, })); const fields: Fields = _fields.reduce((acc, field) => { acc[field.name] = field; return acc; }, {} as Fields); const error = getErrors(content); if (error) setTemplateError(error.message); else setTemplateError(""); return fields; }, [content]); const runScript = useCallback(() => { try { let data: any = {}; Object.keys(fields).forEach(key => { data[key] = fields[key].value; }); const template = generateHtmlTemplate(content, data); setIframeCode(template); state.scriptLogs = [ ...snap.scriptLogs, { type: "success", message: "Started running..." }, ]; } catch (err) { state.scriptLogs = [ ...snap.scriptLogs, // @ts-expect-error { type: "error", message: err?.message || "Could not parse template" }, ]; } }, [content, fields, snap.scriptLogs]); useEffect(() => { const handleEvent = (e: any) => { if (e.data.type === "log" || e.data.type === "error") { const data: ILog[] = e.data.args.map((msg: any) => ({ type: e.data.type, message: typeof msg === "string" ? msg : JSON.stringify(msg, null, 2), })); state.scriptLogs = [...snap.scriptLogs, ...data]; } }; window.addEventListener("message", handleEvent); return () => window.removeEventListener("message", handleEvent); }, [snap.scriptLogs]); useEffect(() => { const defaultFields = getFields() || {}; setFields(defaultFields); }, [content, setFields, getFields]); const accOptions = snap.accounts?.map(acc => ({ ...acc, label: acc.name, value: acc.address, })); const isDisabled = Object.values(fields).some( field => field.required && !field.value ); const handleRun = useCallback(() => { if (isDisabled) return toast.error("Please fill in all the required fields."); state.scriptLogs = []; runScript(); setIsDialogOpen(false); }, [isDisabled, runScript]); return ( <> Run {name} script You are about to run scripts provided by the developer of the hook, make sure you trust the author before you continue. {templateError && ( {templateError} )} {Object.keys(fields).length > 0 && ( Fill in the following parameters to run the script. )} {Object.keys(fields).map(key => { const { name, value, type, description, required } = fields[key]; const isAccount = type?.startsWith("Account"); const isAccountSecret = type === "Account.secret"; const accountField = (isAccount && type?.split(".")[1]) || "address"; return ( {isAccount ? ( { setFields({ ...fields, [key]: { ...fields[key], value: e.target.value }, }); }} /> )} ); })} {iFrameCode && (