import { ArrowOutwardRounded, ClearRounded, Key, Save, Visibility, VisibilityOff, } from '@mui/icons-material' import cloneDeep from 'lodash.clonedeep' import { ComponentType, useCallback, useState } from 'react' import { SubmitErrorHandler, SubmitHandler, useForm, useFormContext, } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { AccountTxForm, AccountTxSave, ActionEncodeContext, LoadingDataWithError, SuspenseLoaderProps, UnifiedCosmosMsg, WalletChainSwitcherProps, } from '@dao-dao/types' import { convertActionKeysAndDataToActions, encodeActions, processError, validateRequired, } from '@dao-dao/utils' import { useActionsContext, useChainContext } from '../../contexts' import { useHoldingKey } from '../../hooks' import { ActionsEditor, ActionsRenderer, RawActionsRenderer } from '../actions' import { Button, ButtonLink } from '../buttons' import { CopyToClipboard } from '../CopyToClipboard' import { IconButton } from '../icon_buttons' import { InputErrorMessage, TextAreaInput, TextInput } from '../inputs' import { Modal } from '../modals' import { Tooltip } from '../tooltip' enum SubmitValue { Preview = 'Preview', Submit = 'Submit', } export type ProfileActionsProps = { execute: (messages: UnifiedCosmosMsg[]) => Promise SuspenseLoader: ComponentType error?: string txHash?: string saves: LoadingDataWithError save: (save: AccountTxSave) => Promise deleteSave: (save: AccountTxSave) => Promise saving: boolean holdingAltForDirectSign: boolean WalletChainSwitcher: ComponentType actionEncodeContext: ActionEncodeContext actionsReadOnlyMode?: boolean } export const ProfileActions = ({ execute, SuspenseLoader, error, txHash, saves, save, deleteSave, saving, holdingAltForDirectSign, WalletChainSwitcher, actionEncodeContext, actionsReadOnlyMode, }: ProfileActionsProps) => { const { t } = useTranslation() const { config } = useChainContext() const { actionMap } = useActionsContext() const { handleSubmit, watch, formState: { errors }, reset, getValues, } = useFormContext() const actionData = watch('actions') || [] const [showPreview, setShowPreview] = useState(false) const [showSubmitErrorNote, setShowSubmitErrorNote] = useState(false) const [submitError, setSubmitError] = useState('') const holdingShiftForForce = useHoldingKey({ key: 'shift' }) const [loading, setLoading] = useState(false) const onSubmitForm: SubmitHandler = useCallback( async ({ actions }, event) => { setShowSubmitErrorNote(false) setSubmitError('') const nativeEvent = event?.nativeEvent as SubmitEvent const submitterValue = (nativeEvent?.submitter as HTMLInputElement)?.value if (submitterValue === SubmitValue.Preview) { setShowPreview((p) => !p) return } setLoading(true) try { const msgs = await encodeActions({ actionMap, encodeContext: actionEncodeContext, data: actions, }) await execute(msgs) } catch (err) { console.error(err) setSubmitError( processError(err, { forceCapture: false, }) ) } finally { setLoading(false) } }, [actionMap, actionEncodeContext, execute] ) const onSubmitError: SubmitErrorHandler = useCallback( async (errors) => { console.error('Form errors', errors) // Attempt submit anyways if forcing. if (holdingShiftForForce) { setLoading(true) try { const msgs = await encodeActions({ actionMap, encodeContext: actionEncodeContext, data: getValues('actions'), }) await execute(msgs) } catch (err) { console.error(err) setSubmitError( processError(err, { forceCapture: false, }) ) } finally { setLoading(false) } // If not forcing, show error to check for errors. } else { setShowSubmitErrorNote(true) setSubmitError('') } }, [actionMap, actionEncodeContext, execute, getValues, holdingShiftForForce] ) const [saveModalVisible, setSaveModalVisible] = useState(false) const { watch: saveWatch, register: saveRegister, handleSubmit: saveHandleSubmit, reset: saveReset, formState: { errors: saveErrors }, } = useForm>({ defaultValues: { name: '', description: '', }, }) const watchSaveName = saveWatch('name') const onSave = async (data: Omit) => { if ( await save({ ...data, // Clone the actions since the save gets cached. We don't want this form // to affect the save once it's been saved. actions: cloneDeep(actionData), }) ) { setSaveModalVisible(false) } } return (
{!actionsReadOnlyMode && (

{t('info.transactionBuilderDescription')}

)}
{actionsReadOnlyMode ? ( ) : ( )}

{t('info.reviewYourTransaction')}

{showSubmitErrorNote && (

{t('error.correctErrorsAbove')}

)} {!!submitError && (

{submitError}

)} {error && (

{error}

)} {txHash && (
{!!config?.explorerUrlTemplates?.tx && ( {t('button.openInChainExplorer')}{' '} )}
)} {showPreview && ( )}

{t('title.saved')}

{t('info.txSavesDescription')}

{!saves.loading && !saves.errored && saves.data.length > 0 ? (
{saves.data.map((save, index) => ( ))}
) : (

{t('info.nothingFound')}

)}
{/* Save modal */} setSaveModalVisible(false)} visible={saveModalVisible} >
{!saves.loading && !saves.errored && saves.data.some(({ name }) => name === watchSaveName) && (

{t('info.overwritingSave')}

)}
) }