import React, { useCallback, useEffect, useState } from 'react' import { Plus, Trash, X } from 'phosphor-react' import { Button, Box, Text } from '.' import { Stack, Flex, Select } from '.' import { Dialog, DialogContent, DialogTitle, DialogDescription, DialogClose, DialogTrigger } from './Dialog' import { Input, Label } from './Input' import { Controller, SubmitHandler, useFieldArray, useForm } from 'react-hook-form' import { deployHook } from '../state/actions' import { useSnapshot } from 'valtio' import state, { IFile, SelectOption } from '../state' import toast from 'react-hot-toast' import { prepareDeployHookTx, sha256 } from '../state/actions/deployHook' import estimateFee from '../utils/estimateFee' import { getParameters, getInvokeOptions, transactionOptions, SetHookData } from '../utils/setHook' import { capitalize } from '../utils/helpers' import AccountSequence from './Sequence' export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo( ({ accountAddress }) => { const snap = useSnapshot(state) const [estimateLoading, setEstimateLoading] = useState(false) const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false) const compiledFiles = snap.files.filter(file => file.compiledContent) const activeFile = compiledFiles[snap.activeWat] as IFile | undefined const accountOptions: SelectOption[] = snap.accounts.map(acc => ({ label: acc.name, value: acc.address })) const [selectedAccount, setSelectedAccount] = useState( accountOptions.find(acc => acc.value === accountAddress) ) const account = snap.accounts.find(acc => acc.address === selectedAccount?.value) const getHookNamespace = useCallback( () => (activeFile && snap.deployValues[activeFile.name]?.HookNamespace) || activeFile?.name.split('.')[0] || '', [activeFile, snap.deployValues] ) const getDefaultValues = useCallback((): Partial => { const content = activeFile?.compiledValueSnapshot return ( (activeFile && snap.deployValues[activeFile.name]) || { HookNamespace: getHookNamespace(), Invoke: getInvokeOptions(content), HookParameters: getParameters(content) } ) }, [activeFile, getHookNamespace, snap.deployValues]) const { register, handleSubmit, control, watch, setValue, getValues, reset, formState: { errors } } = useForm({ defaultValues: getDefaultValues() }) const { fields, append, remove } = useFieldArray({ control, name: 'HookParameters' // unique name for your Field Array }) const watchedFee = watch('Fee') // Reset form if activeFile changes useEffect(() => { if (!activeFile) return const defaultValues = getDefaultValues() reset(defaultValues) }, [activeFile, getDefaultValues, reset]) useEffect(() => { if (watchedFee && (watchedFee.includes('.') || watchedFee.includes(','))) { setValue('Fee', watchedFee.replaceAll('.', '').replaceAll(',', '')) } }, [watchedFee, setValue]) // const { // fields: grantFields, // append: grantAppend, // remove: grantRemove, // } = useFieldArray({ // control, // name: "HookGrants", // unique name for your Field Array // }); const [hashedNamespace, setHashedNamespace] = useState('') const namespace = watch('HookNamespace', getHookNamespace()) const calculateHashedValue = useCallback(async () => { const hashedVal = await sha256(namespace) setHashedNamespace(hashedVal.toUpperCase()) }, [namespace]) useEffect(() => { calculateHashedValue() }, [namespace, calculateHashedValue]) const calculateFee = useCallback(async () => { if (!account) return const formValues = getValues() const tx = await prepareDeployHookTx(account, formValues) if (!tx) { return } const res = await estimateFee(tx, account) if (res && res.base_fee) { setValue('Fee', Math.round(Number(res.base_fee || '')).toString()) } }, [account, getValues, setValue]) const tooLargeFile = () => { return Boolean( activeFile?.compiledContent?.byteLength && activeFile?.compiledContent?.byteLength >= 64000 ) } const onSubmit: SubmitHandler = async data => { const currAccount = state.accounts.find(acc => acc.address === account?.address) if (!account) return if (currAccount) currAccount.isLoading = true data.HookParameters.forEach(param => { delete param.$metaData return param }) const res = await deployHook(account, data) if (currAccount) currAccount.isLoading = false if (res && res.engine_result === 'tesSUCCESS') { toast.success('Transaction succeeded!') return setIsSetHookDialogOpen(false) } toast.error(`Transaction failed! (${res?.engine_result_message})`) } const onOpenChange = useCallback( (open: boolean) => { setIsSetHookDialogOpen(open) if (open) calculateFee() }, [calculateFee] ) return (
Deploy configuration )} /> {errors.HookNamespace?.type === 'required' && ( Namespace is required )} {fields.map((field, index) => ( {errors.HookParameters?.[index]?.HookParameter?.HookParameterValue ?.type === 'required' && This field is required} ))} { if (e.key === '.' || e.key === ',') { e.preventDefault() } }} step="1" defaultValue={10000} css={{ '-moz-appearance': 'textfield', '&::-webkit-outer-spin-button': { '-webkit-appearance': 'none', margin: 0 }, '&::-webkit-inner-spin-button ': { '-webkit-appearance': 'none', margin: 0 } }} /> {errors.Fee?.type === 'required' && ( Fee is required )} {/* {grantFields.map((field, index) => ( ))} */} {/* */} {/* */}
) } ) SetHookDialog.displayName = 'SetHookDialog' export default SetHookDialog