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 = [{ 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 && (