mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
207 lines
6.4 KiB
TypeScript
207 lines
6.4 KiB
TypeScript
import { useRouter } from 'next/router';
|
|
import React from 'react';
|
|
|
|
import { AddressFromToFilterValues } from 'types/api/address';
|
|
import type { CsvExportParams } from 'types/client/address';
|
|
|
|
import type { ResourceName } from 'lib/api/resources';
|
|
import useApiQuery from 'lib/api/useApiQuery';
|
|
import { useMultichainContext } from 'lib/contexts/multichain';
|
|
import throwOnAbsentParamError from 'lib/errors/throwOnAbsentParamError';
|
|
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
|
|
import useIsMobile from 'lib/hooks/useIsMobile';
|
|
import { ContentLoader } from 'toolkit/components/loaders/ContentLoader';
|
|
import { nbsp } from 'toolkit/utils/htmlEntities';
|
|
import CsvExportForm from 'ui/csvExport/CsvExportForm';
|
|
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
|
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
|
|
import ChainIcon from 'ui/shared/externalChains/ChainIcon';
|
|
import PageTitle from 'ui/shared/Page/PageTitle';
|
|
|
|
interface ExportTypeEntity {
|
|
text: string;
|
|
resource: ResourceName;
|
|
fileNameTemplate: string;
|
|
filterType?: CsvExportParams['filterType'];
|
|
filterValues?: Readonly<Array<CsvExportParams['filterValue']>>;
|
|
}
|
|
|
|
const EXPORT_TYPES: Record<CsvExportParams['type'], ExportTypeEntity> = {
|
|
transactions: {
|
|
text: 'transactions',
|
|
resource: 'general:address_csv_export_txs',
|
|
fileNameTemplate: 'transactions',
|
|
filterType: 'address',
|
|
filterValues: AddressFromToFilterValues,
|
|
},
|
|
'internal-transactions': {
|
|
text: 'internal transactions',
|
|
resource: 'general:address_csv_export_internal_txs',
|
|
fileNameTemplate: 'internal_transactions',
|
|
filterType: 'address',
|
|
filterValues: AddressFromToFilterValues,
|
|
},
|
|
'token-transfers': {
|
|
text: 'token transfers',
|
|
resource: 'general:address_csv_export_token_transfers',
|
|
fileNameTemplate: 'token_transfers',
|
|
filterType: 'address',
|
|
filterValues: AddressFromToFilterValues,
|
|
},
|
|
logs: {
|
|
text: 'logs',
|
|
resource: 'general:address_csv_export_logs',
|
|
fileNameTemplate: 'logs',
|
|
filterType: 'topic',
|
|
},
|
|
holders: {
|
|
text: 'holders',
|
|
resource: 'general:token_csv_export_holders',
|
|
fileNameTemplate: 'holders',
|
|
},
|
|
'epoch-rewards': {
|
|
text: 'epoch rewards',
|
|
resource: 'general:address_csv_export_celo_election_rewards',
|
|
fileNameTemplate: 'epoch_rewards',
|
|
},
|
|
};
|
|
|
|
const isCorrectExportType = (type: string): type is CsvExportParams['type'] => Object.keys(EXPORT_TYPES).includes(type);
|
|
|
|
const CsvExport = () => {
|
|
const router = useRouter();
|
|
const isMobile = useIsMobile();
|
|
const multichainContext = useMultichainContext();
|
|
|
|
const addressHash = router.query.address?.toString() || '';
|
|
const exportTypeParam = router.query.type?.toString() || '';
|
|
const exportType = isCorrectExportType(exportTypeParam) ? EXPORT_TYPES[exportTypeParam] : null;
|
|
const filterTypeFromQuery = router.query.filterType?.toString() || null;
|
|
const filterValueFromQuery = router.query.filterValue?.toString();
|
|
|
|
const addressQuery = useApiQuery('general:address', {
|
|
pathParams: { hash: addressHash },
|
|
queryOptions: {
|
|
enabled: Boolean(addressHash),
|
|
},
|
|
});
|
|
|
|
const tokenQuery = useApiQuery('general:token', {
|
|
pathParams: { hash: addressHash },
|
|
queryOptions: {
|
|
enabled: Boolean(addressHash) && exportTypeParam === 'holders',
|
|
},
|
|
});
|
|
|
|
const configQuery = useApiQuery('general:config_csv_export', {
|
|
queryOptions: {
|
|
enabled: Boolean(addressHash),
|
|
},
|
|
});
|
|
|
|
const isLoading = addressQuery.isPending || configQuery.isPending || (exportTypeParam === 'holders' && tokenQuery.isPending);
|
|
|
|
throwOnAbsentParamError(addressHash);
|
|
throwOnAbsentParamError(exportType);
|
|
|
|
if (!exportType) {
|
|
return null;
|
|
}
|
|
|
|
const filterType = filterTypeFromQuery === exportType.filterType ? filterTypeFromQuery : null;
|
|
const filterValue = (() => {
|
|
if (!filterType || !filterValueFromQuery) {
|
|
return null;
|
|
}
|
|
|
|
if (exportType.filterValues && !exportType.filterValues?.includes(filterValueFromQuery)) {
|
|
return null;
|
|
}
|
|
|
|
return filterValueFromQuery;
|
|
})();
|
|
|
|
const content = (() => {
|
|
throwOnResourceLoadError(addressQuery);
|
|
|
|
if (isLoading) {
|
|
return <ContentLoader/>;
|
|
}
|
|
|
|
return (
|
|
<CsvExportForm
|
|
hash={ addressHash }
|
|
resource={ exportType.resource }
|
|
exportType={ isCorrectExportType(exportTypeParam) ? exportTypeParam : undefined }
|
|
filterType={ filterType }
|
|
filterValue={ filterValue }
|
|
fileNameTemplate={ exportType.fileNameTemplate }
|
|
/>
|
|
);
|
|
})();
|
|
|
|
const description = (() => {
|
|
if (isLoading) {
|
|
return null;
|
|
}
|
|
|
|
const chainInfo = multichainContext?.chain ? (
|
|
<div className="items-center inline-flex gap-x-2">
|
|
<span>on</span>
|
|
<ChainIcon data={ multichainContext.chain }/>
|
|
<span>{ multichainContext.chain.app_config.chain.name }</span>
|
|
</div>
|
|
) : null;
|
|
|
|
const limit = (configQuery.data?.limit || 10_000).toLocaleString(undefined, { maximumFractionDigits: 3, notation: 'compact' });
|
|
|
|
if (exportTypeParam === 'holders' && tokenQuery.data) {
|
|
return (
|
|
<div className="flex flex-wrap whitespace-pre-wrap mb-10">
|
|
<span>Export { exportType.text } for token </span>
|
|
<TokenEntity
|
|
token={ tokenQuery.data }
|
|
truncation={ isMobile ? 'constant' : 'dynamic' }
|
|
className="w-fit max-w-full lg:max-w-[400px]"
|
|
noCopy
|
|
noSymbol
|
|
/>
|
|
{ chainInfo }
|
|
<span> to CSV file. </span>
|
|
<span>Exports are limited to the top { limit } holders by amount held.</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!addressQuery.data) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-wrap whitespace-pre-wrap mb-10">
|
|
<span>Export { exportType.text } for address </span>
|
|
<AddressEntity
|
|
address={ addressQuery.data }
|
|
truncation={ isMobile ? 'constant' : 'dynamic' }
|
|
noCopy
|
|
/>
|
|
<span>{ nbsp }</span>
|
|
{ filterType && filterValue && <span>with applied filter by { filterType } ({ filterValue })</span> }
|
|
{ chainInfo }
|
|
<span> to CSV file. </span>
|
|
<span>Exports are limited to the last { limit } { exportType.text }.</span>
|
|
</div>
|
|
);
|
|
})();
|
|
|
|
return (
|
|
<>
|
|
<PageTitle title="Export data to CSV file"/>
|
|
{ description }
|
|
{ content }
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default CsvExport;
|