Files
explore/ui/multichain/address/MultichainAddressInternalTxs.tsx
1a57cb25a0 Async CSV export (#3330)
* add export for address txs

* transfer all export from page to dialog

* remove old route and components

* improve file name and fix loading state of icon button

* basic implementation

* refetch item status

* status indicator for download items

* changes for multichain

* handle too many requests for downloads error

* update button styles and spacing

* fix tests

* disable async export for multichain

* Fix CSV export cancel flow and stabilize context updates

Applied via @cursor push command

* test and review fixes

* design fixes

* refactor file names

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-03-02 15:51:50 +02:00

105 lines
3.4 KiB
TypeScript

import { Box } from '@chakra-ui/react';
import React from 'react';
import type * as multichain from '@blockscout/multichain-aggregator-types';
import CsvExport from 'client/features/csv-export/components/CsvExport';
import multichainConfig from 'configs/multichain';
import { MultichainProvider } from 'lib/contexts/multichain';
import AddressTxsFilter from 'ui/address/AddressTxsFilter';
import useAddressInternalTxsQuery from 'ui/address/useAddressInternalTxsQuery';
import InternalTxsList from 'ui/internalTxs/InternalTxsList';
import InternalTxsTable from 'ui/internalTxs/InternalTxsTable';
import ChainSelect from 'ui/multichain/components/ChainSelect';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
import getAvailableChainIds from './getAvailableChainIds';
interface Props {
addressData: multichain.GetAddressResponse | undefined;
isLoading: boolean;
}
const MultichainAddressInternalTxs = ({ addressData, isLoading }: Props) => {
const chainIds = React.useMemo(() => getAvailableChainIds(addressData), [ addressData ]);
const { hash, query, filterValue, onFilterChange } = useAddressInternalTxsQuery({
enabled: !isLoading && chainIds.length > 0,
isMultichain: true,
chainIds,
});
const { data, isPlaceholderData, isError, pagination, chainValue, onChainValueChange } = query;
const chainData = React.useMemo(() => {
const config = multichainConfig();
return config?.chains.find(({ id }) => id === chainValue?.[0]);
}, [ chainValue ]);
if (chainIds.length === 0) {
return <p>There are no internal transactions.</p>;
}
const content = data?.items ? (
<MultichainProvider chainId={ chainValue?.[0] }>
<Box hideFrom="lg">
<InternalTxsList data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
</Box>
<Box hideBelow="lg">
<InternalTxsTable data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
</Box>
</MultichainProvider>
) : null ;
const actionBar = (
<ActionBar mt={ -6 } justifyContent="left">
<AddressTxsFilter
initialValue={ filterValue }
onFilterChange={ onFilterChange }
hasActiveFilter={ Boolean(filterValue) }
isLoading={ pagination.isLoading }
/>
<ChainSelect
loading={ pagination.isLoading }
value={ chainValue }
onValueChange={ onChainValueChange }
chainIds={ chainIds }
ml={ 2 }
/>
<CsvExport
type="address_internal_txs"
resourceName="general:address_csv_export_internal_txs"
pathParams={{ hash }}
queryParams={ filterValue ? {
filter_type: 'address',
filter_value: filterValue,
} : undefined }
loadingInitial={ pagination.isLoading }
chainData={ chainData }
ml={ 2 }
/>
<Pagination ml="auto" { ...pagination }/>
</ActionBar>
);
return (
<DataListDisplay
isError={ isError }
itemsNum={ data?.items.length }
hasActiveFilters={ Boolean(filterValue) }
emptyStateProps={{
term: 'transaction',
}}
emptyText="There are no internal transactions."
showActionBarIfEmpty
showActionBarIfError
actionBar={ actionBar }
>
{ content }
</DataListDisplay>
);
};
export default React.memo(MultichainAddressInternalTxs);