Commit 4478730c authored by isstuev's avatar isstuev

socket + method

parent 4d737288
...@@ -18,6 +18,7 @@ SocketMessage.AddressCoinBalance | ...@@ -18,6 +18,7 @@ SocketMessage.AddressCoinBalance |
SocketMessage.AddressTxs | SocketMessage.AddressTxs |
SocketMessage.AddressTxsPending | SocketMessage.AddressTxsPending |
SocketMessage.AddressTokenTransfer | SocketMessage.AddressTokenTransfer |
SocketMessage.TokenTransfers |
SocketMessage.Unknown; SocketMessage.Unknown;
interface SocketMessageParamsGeneric<Event extends string | undefined, Payload extends object | unknown> { interface SocketMessageParamsGeneric<Event extends string | undefined, Payload extends object | unknown> {
...@@ -42,5 +43,6 @@ export namespace SocketMessage { ...@@ -42,5 +43,6 @@ export namespace SocketMessage {
export type AddressTxs = SocketMessageParamsGeneric<'transaction', { transaction: Transaction }>; export type AddressTxs = SocketMessageParamsGeneric<'transaction', { transaction: Transaction }>;
export type AddressTxsPending = SocketMessageParamsGeneric<'pending_transaction', { transaction: Transaction }>; export type AddressTxsPending = SocketMessageParamsGeneric<'pending_transaction', { transaction: Transaction }>;
export type AddressTokenTransfer = SocketMessageParamsGeneric<'token_transfer', { token_transfer: TokenTransfer }>; export type AddressTokenTransfer = SocketMessageParamsGeneric<'token_transfer', { token_transfer: TokenTransfer }>;
export type TokenTransfers = SocketMessageParamsGeneric<'token_transfer', {token_transfer: number }>
export type Unknown = SocketMessageParamsGeneric<undefined, unknown>; export type Unknown = SocketMessageParamsGeneric<undefined, unknown>;
} }
...@@ -40,6 +40,7 @@ export const erc20: TokenTransfer = { ...@@ -40,6 +40,7 @@ export const erc20: TokenTransfer = {
timestamp: '2022-10-10T14:34:30.000000Z', timestamp: '2022-10-10T14:34:30.000000Z',
block_hash: '1', block_hash: '1',
log_index: '1', log_index: '1',
method: 'updateSmartAsset',
}; };
export const erc721: TokenTransfer = { export const erc721: TokenTransfer = {
...@@ -81,6 +82,7 @@ export const erc721: TokenTransfer = { ...@@ -81,6 +82,7 @@ export const erc721: TokenTransfer = {
timestamp: '2022-10-10T14:34:30.000000Z', timestamp: '2022-10-10T14:34:30.000000Z',
block_hash: '1', block_hash: '1',
log_index: '1', log_index: '1',
method: 'updateSmartAsset',
}; };
export const erc1155: TokenTransfer = { export const erc1155: TokenTransfer = {
......
...@@ -39,6 +39,7 @@ interface TokenTransferBase { ...@@ -39,6 +39,7 @@ interface TokenTransferBase {
timestamp: string; timestamp: string;
block_hash: string; block_hash: string;
log_index: string; log_index: string;
method?: string;
} }
export type TokenTransferPagination = { export type TokenTransferPagination = {
......
...@@ -10,6 +10,7 @@ import Token from './Token'; ...@@ -10,6 +10,7 @@ import Token from './Token';
const TOKEN_API_URL = buildApiUrl('token', { hash: '1' }); const TOKEN_API_URL = buildApiUrl('token', { hash: '1' });
const TOKEN_COUNTERS_API_URL = buildApiUrl('token_counters', { hash: '1' }); const TOKEN_COUNTERS_API_URL = buildApiUrl('token_counters', { hash: '1' });
const TOKEN_TRANSFERS_API_URL = buildApiUrl('token_transfers', { hash: '1' });
const ADDRESS_API_URL = buildApiUrl('address', { id: '1' }); const ADDRESS_API_URL = buildApiUrl('address', { id: '1' });
const hooksConfig = { const hooksConfig = {
router: { router: {
...@@ -33,6 +34,10 @@ test('base view +@dark-mode', async({ mount, page }) => { ...@@ -33,6 +34,10 @@ test('base view +@dark-mode', async({ mount, page }) => {
status: 200, status: 200,
body: JSON.stringify(tokenCounters), body: JSON.stringify(tokenCounters),
})); }));
await page.route(TOKEN_TRANSFERS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({}),
}));
const component = await mount( const component = await mount(
<TestApp> <TestApp>
......
...@@ -2,7 +2,6 @@ import { Skeleton, Box } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Skeleton, Box } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { TokenInfo } from 'types/api/tokenInfo';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
...@@ -50,7 +49,7 @@ const TokenPageContent = () => { ...@@ -50,7 +49,7 @@ const TokenPageContent = () => {
}); });
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
{ id: 'token_transfers', title: 'Token transfers', component: <TokenTransfer transfersQuery={ transfersQuery } token={ tokenQuery.data as TokenInfo }/> }, { id: 'token_transfers', title: 'Token transfers', component: <TokenTransfer transfersQuery={ transfersQuery } token={ tokenQuery.data }/> },
{ id: 'holders', title: 'Holders', component: <TokenHolders tokenQuery={ tokenQuery } holdersQuery={ holdersQuery }/> }, { id: 'holders', title: 'Holders', component: <TokenHolders tokenQuery={ tokenQuery } holdersQuery={ holdersQuery }/> },
]; ];
......
...@@ -22,6 +22,9 @@ test('erc20 +@mobile', async({ mount }) => { ...@@ -22,6 +22,9 @@ test('erc20 +@mobile', async({ mount }) => {
next_page_params: null, next_page_params: null,
}, },
isPaginationVisible: true, isPaginationVisible: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
pagination: { page: 1 },
}} }}
/> />
</TestApp>, </TestApp>,
...@@ -44,6 +47,9 @@ test('erc721 +@mobile', async({ mount }) => { ...@@ -44,6 +47,9 @@ test('erc721 +@mobile', async({ mount }) => {
next_page_params: null, next_page_params: null,
}, },
isPaginationVisible: true, isPaginationVisible: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
pagination: { page: 1 },
}} }}
/> />
</TestApp>, </TestApp>,
...@@ -66,6 +72,9 @@ test('erc1155 +@mobile', async({ mount }) => { ...@@ -66,6 +72,9 @@ test('erc1155 +@mobile', async({ mount }) => {
next_page_params: null, next_page_params: null,
}, },
isPaginationVisible: true, isPaginationVisible: true,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
pagination: { page: 1 },
}} }}
/> />
</TestApp>, </TestApp>,
......
import { Hide, Show, Text } from '@chakra-ui/react'; import { Hide, Show, Text } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { TokenInfo } from 'types/api/tokenInfo'; import type { TokenInfo } from 'types/api/tokenInfo';
import type { TokenTransferResponse } from 'types/api/tokenTransfer'; import type { TokenTransferResponse } from 'types/api/tokenTransfer';
import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import ActionBar from 'ui/shared/ActionBar'; 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 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';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable'; import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { flattenTotal } from 'ui/shared/TokenTransfer/helpers'; import { flattenTotal } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferList from 'ui/token/TokenTransfer/TokenTransferList'; import TokenTransferList from 'ui/token/TokenTransfer/TokenTransferList';
import TokenTransferTable from 'ui/token/TokenTransfer/TokenTransferTable'; import TokenTransferTable from 'ui/token/TokenTransfer/TokenTransferTable';
type Props = { type Props = {
token: TokenInfo; token?: TokenInfo;
transfersQuery: UseQueryResult<TokenTransferResponse> & { transfersQuery: UseQueryResult<TokenTransferResponse> & {
pagination: PaginationProps; pagination: PaginationProps;
isPaginationVisible: boolean; isPaginationVisible: boolean;
...@@ -26,8 +32,36 @@ type Props = { ...@@ -26,8 +32,36 @@ type Props = {
const TokenTransfer = ({ transfersQuery, token }: Props) => { const TokenTransfer = ({ transfersQuery, token }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const router = useRouter();
const { isError, isLoading, data, pagination, isPaginationVisible } = transfersQuery; const { isError, isLoading, data, pagination, isPaginationVisible } = transfersQuery;
const [ newItemsCount, setNewItemsCount ] = useGradualIncrement(0);
const [ socketAlert, setSocketAlert ] = React.useState('');
const handleNewTransfersMessage: SocketMessage.TokenTransfers['handler'] = (payload) => {
setNewItemsCount(payload.token_transfer);
};
const handleSocketClose = React.useCallback(() => {
setSocketAlert('Connection is lost. Please refresh the page to load new token transfers.');
}, []);
const handleSocketError = React.useCallback(() => {
setSocketAlert('An error has occurred while fetching new token transfers. Please refresh the page.');
}, []);
const channel = useSocketChannel({
topic: `tokens:${ router.query.hash?.toString().toLowerCase() }`,
onSocketClose: handleSocketClose,
onSocketError: handleSocketError,
isDisabled: isLoading || isError || pagination.page !== 1,
});
useSocketMessage({
channel,
event: 'token_transfer',
handler: handleNewTransfersMessage,
});
const content = (() => { const content = (() => {
if (isLoading) { if (isLoading) {
return ( return (
...@@ -55,9 +89,27 @@ const TokenTransfer = ({ transfersQuery, token }: Props) => { ...@@ -55,9 +89,27 @@ const TokenTransfer = ({ transfersQuery, token }: Props) => {
return ( return (
<> <>
<Hide below="lg"> <Hide below="lg">
<TokenTransferTable data={ items } top={ 80 } token={ token }/> <TokenTransferTable
data={ items }
top={ 80 }
// token transfers query depends on token data
// so if we are here, we definitely have token data
token={ token as TokenInfo }
showSocketInfo={ pagination.page === 1 }
socketInfoAlert={ socketAlert }
socketInfoNum={ newItemsCount }
/>
</Hide> </Hide>
<Show below="lg"> <Show below="lg">
{ pagination.page === 1 && (
<SocketNewItemsNotice
url={ window.location.href }
num={ newItemsCount }
alert={ socketAlert }
type="token_transfer"
borderBottomRadius={ 0 }
/>
) }
<TokenTransferList data={ items }/> <TokenTransferList data={ items }/>
</Show> </Show>
</> </>
......
...@@ -11,7 +11,6 @@ import Address from 'ui/shared/address/Address'; ...@@ -11,7 +11,6 @@ import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import ListItemMobile from 'ui/shared/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
type Props = TokenTransfer; type Props = TokenTransfer;
...@@ -22,7 +21,7 @@ const TokenTransferListItem = ({ ...@@ -22,7 +21,7 @@ const TokenTransferListItem = ({
tx_hash: txHash, tx_hash: txHash,
from, from,
to, to,
type, method,
timestamp, timestamp,
}: Props) => { }: Props) => {
const value = (() => { const value = (() => {
...@@ -58,7 +57,7 @@ const TokenTransferListItem = ({ ...@@ -58,7 +57,7 @@ const TokenTransferListItem = ({
</Flex> </Flex>
{ timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> } { timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
</Flex> </Flex>
<Tag colorScheme="orange">{ getTokenTransferTypeText(type) }</Tag> { method && <Tag colorScheme="gray">{ method }</Tag> }
<Flex w="100%" columnGap={ 3 }> <Flex w="100%" columnGap={ 3 }>
<Address width="50%"> <Address width="50%">
<AddressIcon address={ from }/> <AddressIcon address={ from }/>
......
import { Table, Tbody, Tr, Th } from '@chakra-ui/react'; import { Table, Tbody, Tr, Th, Td } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInfo } from 'types/api/tokenInfo'; import type { TokenInfo } from 'types/api/tokenInfo';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { default as Thead } from 'ui/shared/TheadSticky'; import { default as Thead } from 'ui/shared/TheadSticky';
import TokenTransferTableItem from 'ui/token/TokenTransfer/TokenTransferTableItem'; import TokenTransferTableItem from 'ui/token/TokenTransfer/TokenTransferTableItem';
...@@ -11,17 +12,18 @@ interface Props { ...@@ -11,17 +12,18 @@ interface Props {
data: Array<TokenTransfer>; data: Array<TokenTransfer>;
top: number; top: number;
token: TokenInfo; token: TokenInfo;
showSocketInfo: boolean;
socketInfoAlert?: string;
socketInfoNum?: number;
} }
const TokenTransferTable = ({ data, top, token }: Props) => { const TokenTransferTable = ({ data, top, token, showSocketInfo, socketInfoAlert, socketInfoNum }: Props) => {
return ( return (
<Table variant="simple" size="sm"> <Table variant="simple" size="sm">
<Thead top={ top }> <Thead top={ top }>
<Tr> <Tr>
<Th width="40%">Txn hash</Th> <Th width="40%">Txn hash</Th>
{ /* replace with method??? */ } <Th width="164px">Method</Th>
<Th width="164px">Type</Th>
<Th width="148px">From</Th> <Th width="148px">From</Th>
<Th width="36px" px={ 0 }/> <Th width="36px" px={ 0 }/>
<Th width="218px" >To</Th> <Th width="218px" >To</Th>
...@@ -30,6 +32,20 @@ const TokenTransferTable = ({ data, top, token }: Props) => { ...@@ -30,6 +32,20 @@ const TokenTransferTable = ({ data, top, token }: Props) => {
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{ showSocketInfo && (
<Tr>
<Td colSpan={ 10 } p={ 0 }>
<SocketNewItemsNotice
borderRadius={ 0 }
pl="10px"
url={ window.location.href }
alert={ socketInfoAlert }
num={ socketInfoNum }
type="token_transfer"
/>
</Td>
</Tr>
) }
{ data.map((item, index) => ( { data.map((item, index) => (
<TokenTransferTableItem key={ index } { ...item }/> <TokenTransferTableItem key={ index } { ...item }/>
)) } )) }
......
...@@ -9,7 +9,6 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; ...@@ -9,7 +9,6 @@ import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
type Props = TokenTransfer type Props = TokenTransfer
...@@ -20,7 +19,7 @@ const TokenTransferTableItem = ({ ...@@ -20,7 +19,7 @@ const TokenTransferTableItem = ({
tx_hash: txHash, tx_hash: txHash,
from, from,
to, to,
type, method,
timestamp, timestamp,
}: Props) => { }: Props) => {
const value = (() => { const value = (() => {
...@@ -42,7 +41,7 @@ const TokenTransferTableItem = ({ ...@@ -42,7 +41,7 @@ const TokenTransferTableItem = ({
{ timestamp && <Text color="gray.500" fontWeight="400" mt="10px">{ timeAgo }</Text> } { timestamp && <Text color="gray.500" fontWeight="400" mt="10px">{ timeAgo }</Text> }
</Td> </Td>
<Td> <Td>
<Tag colorScheme="orange">{ getTokenTransferTypeText(type) }</Tag> { method ? <Tag colorScheme="gray">{ method }</Tag> : '-' }
</Td> </Td>
<Td> <Td>
<Address display="inline-flex" maxW="100%" lineHeight="30px"> <Address display="inline-flex" maxW="100%" lineHeight="30px">
......
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