mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
199 lines
7.3 KiB
TypeScript
199 lines
7.3 KiB
TypeScript
import { useRouter } from 'next/router';
|
|
import React from 'react';
|
|
|
|
import type * as multichain from '@luxfi/multichain-aggregator-types';
|
|
import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types';
|
|
|
|
import multichainConfig from 'configs/multichain';
|
|
import getSocketUrl from 'lib/api/getSocketUrl';
|
|
import { MultichainProvider } from 'lib/contexts/multichain';
|
|
import useIsMobile from 'lib/hooks/useIsMobile';
|
|
import getQueryParamString from 'lib/router/getQueryParamString';
|
|
import { SocketProvider } from 'lib/socket/context';
|
|
import { EmptyState } from '@luxfi/ui/empty-state';
|
|
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
|
import AddressAdvancedFilterLink from 'ui/address/AddressAdvancedFilterLink';
|
|
import AddressCsvExportLink from 'ui/address/AddressCsvExportLink';
|
|
import AddressTokenTransfersLocal from 'ui/address/AddressTokenTransfersLocal';
|
|
import useAddressTokenTransfersQuery from 'ui/address/useAddressTokenTransfersQuery';
|
|
import useAddressCountersQuery from 'ui/address/utils/useAddressCountersQuery';
|
|
import ChainSelect from 'ui/multichain/components/ChainSelect';
|
|
import Pagination from 'ui/shared/pagination/Pagination';
|
|
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
|
|
import { getTokenFilterValue } from 'ui/tokens/utils';
|
|
|
|
import ListCounterText from '../components/ListCounterText';
|
|
import getAvailableChainIds from './getAvailableChainIds';
|
|
|
|
export const ADDRESS_MULTICHAIN_TOKEN_TRANSFERS_TAB_IDS = [ 'token_transfers_cross_chain' as const, 'token_transfers_local' as const ];
|
|
const TABS_RIGHT_SLOT_PROPS = {
|
|
className: 'flex justify-end lg:justify-between ml-0 lg:ml-8',
|
|
widthAllocation: 'available' as const,
|
|
};
|
|
const TAB_LIST_PROPS = {
|
|
marginBottom: 0,
|
|
pt: 6,
|
|
pb: 3,
|
|
marginTop: -6,
|
|
};
|
|
|
|
interface Props {
|
|
addressData: multichain.GetAddressResponse | undefined;
|
|
isLoading: boolean;
|
|
}
|
|
|
|
const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => {
|
|
const router = useRouter();
|
|
const isMobile = useIsMobile();
|
|
|
|
const hash = getQueryParamString(router.query.hash);
|
|
const tab = getQueryParamString(router.query.tab) as typeof ADDRESS_MULTICHAIN_TOKEN_TRANSFERS_TAB_IDS[number] | 'token_transfers' | undefined;
|
|
const isLocalTab = tab === 'token_transfers_local' || tab === 'token_transfers';
|
|
|
|
const chainIds = React.useMemo(() => getAvailableChainIds(addressData), [ addressData ]);
|
|
|
|
const transfersQueryLocal = useAddressTokenTransfersQuery({
|
|
currentAddress: hash,
|
|
enabled: isLocalTab && !isLoading && chainIds.length > 0,
|
|
isMultichain: true,
|
|
chainIds,
|
|
});
|
|
|
|
const chainId = transfersQueryLocal.query.chainValue?.[0];
|
|
const chainData = multichainConfig()?.chains.find(chain => chain.id === chainId);
|
|
|
|
const countersQueryLocal = useAddressCountersQuery({
|
|
hash,
|
|
isLoading: transfersQueryLocal.query.isPlaceholderData,
|
|
isEnabled: isLocalTab && !isLoading && chainIds.length > 0,
|
|
chain: chainData,
|
|
});
|
|
|
|
const handleChainValueChange = React.useCallback(({ value }: { value: Array<string> }) => {
|
|
transfersQueryLocal.query.onChainValueChange({ value });
|
|
const chainConfig = multichainConfig()?.chains.find(chain => chain.id === value[0]);
|
|
const tokenTypes = getTokenFilterValue(router.query.type, chainConfig?.app_config);
|
|
if (tokenTypes) {
|
|
const chainsTokenTypes = transfersQueryLocal.filters.type.filter(type => tokenTypes.includes(type));
|
|
if (chainsTokenTypes.length < transfersQueryLocal.filters.type.length) {
|
|
transfersQueryLocal.onTypeFilterChange(chainsTokenTypes);
|
|
}
|
|
}
|
|
}, [ transfersQueryLocal, router.query.type ]);
|
|
|
|
const countersText = (() => {
|
|
if (isLocalTab) {
|
|
return (
|
|
<ListCounterText
|
|
key={ chainId }
|
|
value={ countersQueryLocal.data?.token_transfers_count }
|
|
isLoading={ countersQueryLocal.isPlaceholderData || transfersQueryLocal.query.isPlaceholderData }
|
|
type="transfer"
|
|
/>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
})();
|
|
|
|
const rightSlot = (() => {
|
|
if (isLocalTab) {
|
|
if (chainIds.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const chainSelect = (
|
|
<ChainSelect
|
|
loading={ transfersQueryLocal.query.pagination.isLoading }
|
|
value={ transfersQueryLocal.query.chainValue }
|
|
onValueChange={ handleChainValueChange }
|
|
chainIds={ chainIds }
|
|
/>
|
|
);
|
|
|
|
if (isMobile) {
|
|
return chainSelect;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="flex gap-2">
|
|
<TokenTransferFilter
|
|
defaultTypeFilters={ transfersQueryLocal.filters.type }
|
|
onTypeFilterChange={ transfersQueryLocal.onTypeFilterChange }
|
|
appliedFiltersNum={ transfersQueryLocal.filters.type.length + (transfersQueryLocal.filters.filter ? 1 : 0) }
|
|
withAddressFilter
|
|
onAddressFilterChange={ transfersQueryLocal.onAddressFilterChange }
|
|
defaultAddressFilter={ transfersQueryLocal.filters.filter }
|
|
isLoading={ transfersQueryLocal.query.isPlaceholderData }
|
|
chainConfig={ chainData?.app_config }
|
|
/>
|
|
{ chainSelect }
|
|
{ countersText }
|
|
</div>
|
|
<div className="flex gap-6">
|
|
<AddressAdvancedFilterLink
|
|
isLoading={ transfersQueryLocal.query.isPlaceholderData }
|
|
address={ hash }
|
|
typeFilter={ transfersQueryLocal.filters.type }
|
|
directionFilter={ transfersQueryLocal.filters.filter }
|
|
chainData={ chainData }
|
|
/>
|
|
<AddressCsvExportLink
|
|
address={ hash }
|
|
params={{ type: 'token-transfers', filterType: 'address', filterValue: transfersQueryLocal.filters.filter }}
|
|
isLoading={ transfersQueryLocal.query.pagination.isLoading }
|
|
chainData={ chainData }
|
|
/>
|
|
<Pagination { ...transfersQueryLocal.query.pagination }/>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
})();
|
|
|
|
const tabs: Array<TabItemRegular> = [
|
|
{
|
|
id: 'token_transfers_cross_chain',
|
|
title: 'Cross-chain',
|
|
component: <EmptyState type="coming_soon"/>,
|
|
},
|
|
{
|
|
id: [ 'token_transfers_local', 'token_transfers' ],
|
|
title: 'Local',
|
|
component: chainIds.length > 0 ? (
|
|
<MultichainProvider chainId={ transfersQueryLocal.query.chainValue?.[0] }>
|
|
<SocketProvider url={ getSocketUrl(chainData?.app_config) }>
|
|
{ isMobile && countersText }
|
|
<AddressTokenTransfersLocal
|
|
query={ transfersQueryLocal.query }
|
|
filters={ transfersQueryLocal.filters }
|
|
onTypeFilterChange={ transfersQueryLocal.onTypeFilterChange }
|
|
onAddressFilterChange={ transfersQueryLocal.onAddressFilterChange }
|
|
addressHash={ hash }
|
|
/>
|
|
</SocketProvider>
|
|
</MultichainProvider>
|
|
) : <p>There are no token transfers.</p>,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<RoutedTabs
|
|
variant="secondary"
|
|
size="sm"
|
|
tabs={ tabs }
|
|
isLoading={ isLoading }
|
|
defaultTabId="token_transfers_local"
|
|
rightSlot={ rightSlot }
|
|
rightSlotProps={ TABS_RIGHT_SLOT_PROPS }
|
|
listProps={ isMobile ? undefined : TAB_LIST_PROPS }
|
|
stickyEnabled={ !isMobile }
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default React.memo(MultichainAddressTokenTransfers);
|