import { FC, useCallback, useEffect, useMemo, useState } from 'react' import Container from '../Container' import Flex from '../Flex' import Input from '../Input' import Select from '../Select' import Text from '../Text' import { SelectOption, TransactionState, transactionsOptions, TxFields, getTxFields, defaultTransactionType } from '../../state/transactions' import { useSnapshot } from 'valtio' import state from '../../state' import { streamState } from '../DebugStream' import { Button } from '..' import Textarea from '../Textarea' import { getFlags } from '../../state/constants/flags' interface UIProps { setState: (pTx?: Partial | undefined) => TransactionState | undefined resetState: (tt?: SelectOption) => TransactionState | undefined state: TransactionState estimateFee?: (...arg: any) => Promise } export const TxUI: FC = ({ state: txState, setState, resetState, estimateFee }) => { const { accounts } = useSnapshot(state) const { selectedAccount, selectedDestAccount, selectedTransaction, txFields, selectedFlags } = txState const accountOptions: SelectOption[] = accounts.map(acc => ({ label: acc.name, value: acc.address })) const destAccountOptions: SelectOption[] = accounts .map(acc => ({ label: acc.name, value: acc.address })) .filter(acc => acc.value !== selectedAccount?.value) const flagsOptions: SelectOption[] = Object.entries( getFlags(selectedTransaction?.value) || {} ).map(([label, value]) => ({ label, value })) const [feeLoading, setFeeLoading] = useState(false) const handleSetAccount = (acc: SelectOption) => { setState({ selectedAccount: acc }) streamState.selectedAccount = acc } const handleSetField = useCallback( (field: keyof TxFields, value: string, opFields?: TxFields) => { const fields = opFields || txFields const obj = fields[field] setState({ txFields: { ...fields, [field]: typeof obj === 'object' ? { ...obj, $value: value } : value } }) }, [setState, txFields] ) const handleEstimateFee = useCallback( async (state?: TransactionState, silent?: boolean) => { setFeeLoading(true) const fee = await estimateFee?.(state, { silent }) if (fee) handleSetField('Fee', fee, state?.txFields) setFeeLoading(false) }, [estimateFee, handleSetField] ) const handleChangeTxType = useCallback( (tt: SelectOption) => { setState({ selectedTransaction: tt }) const newState = resetState(tt) handleEstimateFee(newState, true) }, [handleEstimateFee, resetState, setState] ) const switchToJson = () => setState({ viewType: 'json' }) // default tx useEffect(() => { if (selectedTransaction?.value) return if (defaultTransactionType) { handleChangeTxType(defaultTransactionType) } }, [handleChangeTxType, selectedTransaction?.value]) const fields = useMemo( () => getTxFields(selectedTransaction?.value), [selectedTransaction?.value] ) const richFields = ['TransactionType', 'Account'] if (fields.Destination !== undefined) { richFields.push('Destination') } if (flagsOptions.length) { richFields.push('Flags') } const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields] return ( Transaction type:{' '} handleSetAccount(acc)} // TODO make react-select have correct types for acc /> {richFields.includes('Destination') && ( Destination account:{' '} setState({ selectedFlags: flags as any })} closeMenuOnSelect={ selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false } /> )} {otherFields.map(field => { let _value = txFields[field] let value: string | undefined if (typeof _value === 'object') { if (_value.$type === 'json' && typeof _value.$value === 'object') { value = JSON.stringify(_value.$value, null, 2) } else { value = _value.$value.toString() } } else { value = _value?.toString() } const isXrp = typeof _value === 'object' && _value.$type === 'xrp' const isJson = typeof _value === 'object' && _value.$type === 'json' const isFee = field === 'Fee' let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined if (rows && rows > 5) rows = 5 return ( {field + (isXrp ? ' (XRP)' : '')}:{' '} {isJson ? (