Commit 6f3aa313 authored by tom's avatar tom

csv export link for address data

parent 65fd3abf
...@@ -20,5 +20,6 @@ ...@@ -20,5 +20,6 @@
"search_results": "/search-results", "search_results": "/search-results",
"auth": "/auth/auth0", "auth": "/auth/auth0",
"stats": "/stats", "stats": "/stats",
"visualize_sol2uml": "/visualize/sol2uml" "visualize_sol2uml": "/visualize/sol2uml",
"csv_export": "/csv-export"
} }
...@@ -99,6 +99,10 @@ export const ROUTES = { ...@@ -99,6 +99,10 @@ export const ROUTES = {
pattern: PATHS.visualize_sol2uml, pattern: PATHS.visualize_sol2uml,
}, },
csv_export: {
pattern: PATHS.csv_export,
},
// AUTH // AUTH
auth: { auth: {
pattern: PATHS.auth, pattern: PATHS.auth,
......
import { chakra, Icon, Link, Tooltip } from '@chakra-ui/react';
import React from 'react';
import svgFileIcon from 'icons/svg_file.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import link from 'lib/link/link';
interface Props {
address: string;
type: 'transactions' | 'internal-transactions' | 'token-transfers';
className?: string;
}
const AddressCsvExportLink = ({ className, address, type }: Props) => {
const isMobile = useIsMobile();
return (
<Tooltip isDisabled={ !isMobile } label="Download CSV">
<Link
className={ className }
display="inline-flex"
alignItems="center"
href={ link('csv_export', undefined, { type, address }) }
>
<Icon as={ svgFileIcon } boxSize={ 6 }/>
{ !isMobile && <chakra.span ml={ 1 }>Download CSV</chakra.span> }
</Link>
</Tooltip>
);
};
export default React.memo(chakra(AddressCsvExportLink));
...@@ -17,6 +17,7 @@ import ActionBar from 'ui/shared/ActionBar'; ...@@ -17,6 +17,7 @@ import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import AddressCsvExportLink from './AddressCsvExportLink';
import AddressTxsFilter from './AddressTxsFilter'; import AddressTxsFilter from './AddressTxsFilter';
import AddressIntTxsList from './internals/AddressIntTxsList'; import AddressIntTxsList from './internals/AddressIntTxsList';
...@@ -80,13 +81,14 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE ...@@ -80,13 +81,14 @@ const AddressInternalTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivE
return ( return (
<> <>
<ActionBar mt={ -6 }> <ActionBar mt={ -6 } justifyContent="left">
<AddressTxsFilter <AddressTxsFilter
defaultFilter={ filterValue } defaultFilter={ filterValue }
onFilterChange={ handleFilterChange } onFilterChange={ handleFilterChange }
isActive={ Boolean(filterValue) } isActive={ Boolean(filterValue) }
/> />
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } <AddressCsvExportLink address={ queryIdStr } type="internal-transactions" ml={{ base: 2, lg: 'auto' }}/>
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> }
</ActionBar> </ActionBar>
{ content } { content }
</> </>
......
...@@ -28,6 +28,8 @@ import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; ...@@ -28,6 +28,8 @@ import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList'; import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList';
import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable'; import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable';
import AddressCsvExportLink from './AddressCsvExportLink';
type Filters = { type Filters = {
type: Array<TokenType>; type: Array<TokenType>;
filter: AddressFromToFilter | undefined; filter: AddressFromToFilter | undefined;
...@@ -217,7 +219,8 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -217,7 +219,8 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
onAddressFilterChange={ handleAddressFilterChange } onAddressFilterChange={ handleAddressFilterChange }
defaultAddressFilter={ filters.filter } defaultAddressFilter={ filters.filter }
/> />
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> } { currentAddress && <AddressCsvExportLink address={ currentAddress } type="token-transfers" ml={{ base: 2, lg: 'auto' }}/> }
{ isPaginationVisible && <Pagination ml={{ base: 'auto', lg: 8 }} { ...pagination }/> }
</ActionBar> </ActionBar>
) } ) }
{ content } { content }
......
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import castArray from 'lodash/castArray';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -17,6 +16,7 @@ import ActionBar from 'ui/shared/ActionBar'; ...@@ -17,6 +16,7 @@ import ActionBar from 'ui/shared/ActionBar';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
import TxsContent from 'ui/txs/TxsContent'; import TxsContent from 'ui/txs/TxsContent';
import AddressCsvExportLink from './AddressCsvExportLink';
import AddressTxsFilter from './AddressTxsFilter'; import AddressTxsFilter from './AddressTxsFilter';
const OVERLOAD_COUNT = 75; const OVERLOAD_COUNT = 75;
...@@ -31,12 +31,13 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>} ...@@ -31,12 +31,13 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}
const [ newItemsCount, setNewItemsCount ] = React.useState(0); const [ newItemsCount, setNewItemsCount ] = React.useState(0);
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const currentAddress = router.query.id?.toString();
const [ filterValue, setFilterValue ] = React.useState<AddressFromToFilter>(getFilterValue(router.query.filter)); const [ filterValue, setFilterValue ] = React.useState<AddressFromToFilter>(getFilterValue(router.query.filter));
const addressTxsQuery = useQueryWithPages({ const addressTxsQuery = useQueryWithPages({
resourceName: 'address_txs', resourceName: 'address_txs',
pathParams: { id: castArray(router.query.id)[0] }, pathParams: { id: currentAddress },
filters: { filter: filterValue }, filters: { filter: filterValue },
scrollRef, scrollRef,
}); });
...@@ -51,8 +52,6 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>} ...@@ -51,8 +52,6 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}
const handleNewSocketMessage: SocketMessage.AddressTxs['handler'] = (payload) => { const handleNewSocketMessage: SocketMessage.AddressTxs['handler'] = (payload) => {
setSocketAlert(''); setSocketAlert('');
const currentAddress = router.query.id?.toString();
if (addressTxsQuery.data?.items && addressTxsQuery.data.items.length >= OVERLOAD_COUNT) { if (addressTxsQuery.data?.items && addressTxsQuery.data.items.length >= OVERLOAD_COUNT) {
if ( if (
!filterValue || !filterValue ||
...@@ -109,7 +108,7 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>} ...@@ -109,7 +108,7 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}
}, []); }, []);
const channel = useSocketChannel({ const channel = useSocketChannel({
topic: `addresses:${ (router.query.id as string).toLowerCase() }`, topic: `addresses:${ currentAddress?.toLowerCase() }`,
onSocketClose: handleSocketClose, onSocketClose: handleSocketClose,
onSocketError: handleSocketError, onSocketError: handleSocketError,
isDisabled: addressTxsQuery.pagination.page !== 1, isDisabled: addressTxsQuery.pagination.page !== 1,
...@@ -140,13 +139,14 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>} ...@@ -140,13 +139,14 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}
{ !isMobile && ( { !isMobile && (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
{ filter } { filter }
{ addressTxsQuery.isPaginationVisible && <Pagination { ...addressTxsQuery.pagination }/> } { currentAddress && <AddressCsvExportLink address={ currentAddress } type="transactions" ml="auto"/> }
{ addressTxsQuery.isPaginationVisible && <Pagination { ...addressTxsQuery.pagination } ml={ 8 }/> }
</ActionBar> </ActionBar>
) } ) }
<TxsContent <TxsContent
filter={ filter } filter={ filter }
query={ addressTxsQuery } query={ addressTxsQuery }
currentAddress={ typeof router.query.id === 'string' ? router.query.id : undefined } currentAddress={ typeof currentAddress === 'string' ? currentAddress : undefined }
enableTimeIncrement enableTimeIncrement
showSocketInfo={ addressTxsQuery.pagination.page === 1 } showSocketInfo={ addressTxsQuery.pagination.page === 1 }
socketInfoAlert={ socketAlert } socketInfoAlert={ socketAlert }
......
...@@ -5,6 +5,7 @@ import React from 'react'; ...@@ -5,6 +5,7 @@ import React from 'react';
import type { TxsResponse } from 'types/api/transaction'; import type { TxsResponse } from 'types/api/transaction';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import AddressCsvExportLink from 'ui/address/AddressCsvExportLink';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import type { Props as PaginationProps } from 'ui/shared/Pagination'; import type { Props as PaginationProps } from 'ui/shared/Pagination';
import SkeletonList from 'ui/shared/skeletons/SkeletonList'; import SkeletonList from 'ui/shared/skeletons/SkeletonList';
...@@ -123,6 +124,7 @@ const TxsContent = ({ ...@@ -123,6 +124,7 @@ const TxsContent = ({
paginationProps={ query.pagination } paginationProps={ query.pagination }
showPagination={ query.isPaginationVisible } showPagination={ query.isPaginationVisible }
filterComponent={ filter } filterComponent={ filter }
linkSlot={ currentAddress ? <AddressCsvExportLink address={ currentAddress } type="transactions" ml={ 2 }/> : null }
/> />
) } ) }
{ content } { content }
......
...@@ -18,9 +18,10 @@ type Props = { ...@@ -18,9 +18,10 @@ type Props = {
className?: string; className?: string;
showPagination?: boolean; showPagination?: boolean;
filterComponent?: React.ReactNode; filterComponent?: React.ReactNode;
linkSlot?: React.ReactNode;
} }
const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps, className, showPagination = true }: Props) => { const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps, className, showPagination = true, linkSlot }: Props) => {
return ( return (
<ActionBar className={ className }> <ActionBar className={ className }>
<HStack> <HStack>
...@@ -38,6 +39,7 @@ const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps ...@@ -38,6 +39,7 @@ const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps
size="xs" size="xs"
placeholder="Search by addresses, hash, method..." placeholder="Search by addresses, hash, method..."
/> */ } /> */ }
{ linkSlot }
</HStack> </HStack>
{ showPagination && <Pagination { ...paginationProps }/> } { showPagination && <Pagination { ...paginationProps }/> }
</ActionBar> </ActionBar>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment