import toast from 'react-hot-toast' import { useSnapshot } from 'valtio' import { ArrowSquareOut, Copy, Trash, Wallet, X } from 'phosphor-react' import React, { useEffect, useState, FC } from 'react' import Dinero from 'dinero.js' import Button from './Button' import { addFaucetAccount, importAccount } from '../state/actions' import state from '../state' import Box from './Box' import { Container, Heading, Stack, Text, Flex } from '.' import { Dialog, DialogContent, DialogTitle, DialogDescription, DialogClose, DialogTrigger } from './Dialog' import { css } from '../stitches.config' import { Input, Label } from './Input' import truncate from '../utils/truncate' const labelStyle = css({ color: '$mauve10', textTransform: 'uppercase', fontSize: '10px', mb: '$0.5' }) import transactionsData from '../content/transactions.json' import { SetHookDialog } from './SetHookDialog' import { addFunds } from '../state/actions/addFaucetAccount' import { deleteHook } from '../state/actions/deployHook' import { capitalize } from '../utils/helpers' import { deleteAccount } from '../state/actions/deleteAccount' export const AccountDialog = ({ activeAccountAddress, setActiveAccountAddress }: { activeAccountAddress: string | null setActiveAccountAddress: React.Dispatch> }) => { const snap = useSnapshot(state) const [showSecret, setShowSecret] = useState(false) const activeAccount = snap.accounts.find(account => account.address === activeAccountAddress) return ( { setShowSecret(false) !open && setActiveAccountAddress(null) }} > {activeAccount?.name} Account Address {activeAccount?.address} Secret {showSecret ? activeAccount?.secret : '•'.repeat(activeAccount?.secret.length || 16)}{' '} Balances & Objects {Dinero({ amount: Number(activeAccount?.xrp || '0'), precision: 6 }) .toUnit() .toLocaleString(undefined, { style: 'currency', currency: 'XRP', currencyDisplay: 'name' })} Installed Hooks {activeAccount && activeAccount.hooks.length > 0 ? activeAccount.hooks.map(i => { return ( {truncate(i, 12)} ) }) : '–'} {activeAccount && activeAccount?.hooks?.length > 0 && ( )} ) } interface AccountProps { card?: boolean hideDeployBtn?: boolean showHookStats?: boolean } const Accounts: FC = props => { const snap = useSnapshot(state) const [activeAccountAddress, setActiveAccountAddress] = useState(null) useEffect(() => { const fetchAccInfo = async () => { if (snap.clientStatus === 'online') { const requests = snap.accounts.map(acc => snap.client?.send({ id: `hooks-builder-req-info-${acc.address}`, command: 'account_info', account: acc.address }) ) const responses = await Promise.all(requests) responses.forEach((res: any) => { const address = res?.account_data?.Account as string const balance = res?.account_data?.Balance as string const sequence = res?.account_data?.Sequence as number const accountToUpdate = state.accounts.find(acc => acc.address === address) if (accountToUpdate) { accountToUpdate.xrp = balance accountToUpdate.sequence = sequence accountToUpdate.error = null } else { const oldAccount = state.accounts.find(acc => acc.address === res?.account) if (oldAccount) { oldAccount.xrp = '0' oldAccount.error = { code: res?.error, message: res?.error_message } } } }) const objectRequests = snap.accounts.map(acc => { return snap.client?.send({ id: `hooks-builder-req-objects-${acc.address}`, command: 'account_objects', account: acc.address }) }) const objectResponses = await Promise.all(objectRequests) objectResponses.forEach((res: any) => { const address = res?.account as string const accountToUpdate = state.accounts.find(acc => acc.address === address) if (accountToUpdate) { accountToUpdate.hooks = res.account_objects .find((ac: any) => ac?.LedgerEntryType === 'Hook') ?.Hooks?.map((oo: any) => oo.Hook.HookHash) || [] } }) } } let fetchAccountInfoInterval: NodeJS.Timer if (snap.clientStatus === 'online') { fetchAccInfo() fetchAccountInfoInterval = setInterval(() => fetchAccInfo(), 10000) } return () => { if (snap.accounts.length > 0) { if (fetchAccountInfoInterval) { clearInterval(fetchAccountInfoInterval) } } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [snap.accounts.length, snap.clientStatus]) return ( Accounts {snap.accounts.map(account => ( setActiveAccountAddress(account.address)} css={{ px: '$3', py: props.card ? '$3' : '$2', cursor: 'pointer', borderBottom: props.card ? '1px solid $mauve6' : undefined, '@hover': { '&:hover': { background: '$backgroundAlt' } } }} > {account.name} {account.address}{' '} {!account?.error ? ( `(${Dinero({ amount: Number(account?.xrp || '0'), precision: 6 }) .toUnit() .toLocaleString(undefined, { style: 'currency', currency: 'XRP', currencyDisplay: 'name' })})` ) : ( (Account not found, request funds to activate account) )} {!props.hideDeployBtn && (
{ e.stopPropagation() }} >
)}
{props.showHookStats && ( {account.hooks.length} hook {account.hooks.length === 1 ? '' : 's'} installed )}
))}
) } export const transactionsOptions = transactionsData.map(tx => ({ value: tx.TransactionType, label: tx.TransactionType })) const ImportAccountDialog = ({ type = 'import' }: { type?: 'import' | 'create' }) => { const [secret, setSecret] = useState('') const [name, setName] = useState('') const btnText = type === 'import' ? 'Import' : 'Create' const title = type === 'import' ? 'Import Account' : 'Create Account' const handleSubmit = async () => { if (type === 'create') { const value = capitalize(name) await addFaucetAccount(value, true) setName('') setSecret('') return } importAccount(secret, name) setName('') setSecret('') } return ( {title} setName(e.target.value)} /> {type === 'import' && ( setSecret(e.target.value)} /> )} ) } export default Accounts