Commit 8b1723ce authored by tom's avatar tom

socket for tx page

parent 1d3a8a0c
......@@ -11,7 +11,7 @@ const BlockTxs = () => {
return (
<TxsContent
queryName={ QueryKeys.blockTxs }
apiPath={ `/api/blocks/${ router.query.id }/transactions` }
apiPath={ `/node-api/blocks/${ router.query.id }/transactions` }
/>
);
};
......
......@@ -35,7 +35,7 @@ const TransactionPageContent = () => {
const { data } = useQuery<unknown, unknown, Transaction>(
[ 'tx', router.query.id ],
async() => await fetch(`/api/transactions/${ router.query.id }`),
async() => await fetch(`/node-api/transactions/${ router.query.id }`),
{
enabled: Boolean(router.query.id),
},
......@@ -48,7 +48,7 @@ const TransactionPageContent = () => {
return <ExternalLink key={ explorer.baseUrl } title={ `Open in ${ explorer.title }` } href={ url.toString() }/>;
});
const hasGoBackLink = isBrowser() && window.document.referrer.includes('/txs');
const hasGoBackLink = false && isBrowser() && window.document.referrer.includes('/txs');
return (
<Page>
......
import { Grid, GridItem, Text, Box, Icon, Link, Spinner, Tag, Flex, Tooltip, Alert, chakra } from '@chakra-ui/react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { Grid, GridItem, Text, Box, Icon, Link, Spinner, Tag, Flex, Tooltip, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import { useRouter } from 'next/router';
import React from 'react';
import { scroller, Element } from 'react-scroll';
import type { SocketMessage } from 'lib/socket/types';
import type { Transaction } from 'types/api/transaction';
import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config';
import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg';
......@@ -16,9 +10,6 @@ import errorIcon from 'icons/status/error.svg';
import successIcon from 'icons/status/success.svg';
import { WEI, WEI_IN_GWEI } from 'lib/consts';
import dayjs from 'lib/date/dayjs';
import useFetch from 'lib/hooks/useFetch';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
import getConfirmationDuration from 'lib/tx/getConfirmationDuration';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
......@@ -31,13 +22,14 @@ import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
// import PrevNext from 'ui/shared/PrevNext';
import RawInputData from 'ui/shared/RawInputData';
import TextSeparator from 'ui/shared/TextSeparator';
// import TokenSnippet from 'ui/shared/TokenSnippet';
import TxStatus from 'ui/shared/TxStatus';
import Utilization from 'ui/shared/Utilization';
import TxDetailsSkeleton from 'ui/tx/details/TxDetailsSkeleton';
import TxRevertReason from 'ui/tx/details/TxRevertReason';
import TokenTransferList from 'ui/tx/TokenTransferList';
import TxDecodedInputData from 'ui/tx/TxDecodedInputData';
import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const TOKEN_TRANSFERS = [
{ title: 'Tokens Transferred', hint: 'List of tokens transferred in the transaction.', type: 'token_transfer' },
......@@ -47,42 +39,7 @@ const TOKEN_TRANSFERS = [
];
const TxDetails = () => {
const router = useRouter();
const fetch = useFetch();
const queryClient = useQueryClient();
const [ socketAlert, setSocketAlert ] = React.useState('');
const { data, isLoading, isError } = useQuery<unknown, unknown, Transaction>(
[ QueryKeys.tx, router.query.id ],
async() => await fetch(`/node-api/transactions/${ router.query.id }`),
{
enabled: Boolean(router.query.id),
},
);
const handleStatusUpdateMessage: SocketMessage.TxStatusUpdate['handler'] = React.useCallback(() => {
queryClient.invalidateQueries({ queryKey: [ QueryKeys.tx, router.query.id ] });
}, [ queryClient, router.query.id ]);
const handleSocketClose = React.useCallback(() => {
setSocketAlert('Connection is lost. Please click here to update transaction info.');
}, []);
const handleSocketError = React.useCallback(() => {
setSocketAlert('An error has occurred while fetching new blocks. Please click here to update transaction info.');
}, []);
const channel = useSocketChannel({
topic: `transactions:${ router.query.id }`,
onSocketClose: handleSocketClose,
onSocketError: handleSocketError,
isDisabled: isLoading || isError || data.status !== null,
});
useSocketMessage({
channel,
event: 'collated',
handler: handleStatusUpdateMessage,
});
const { data, isLoading, isError, socketStatus } = useFetchTxInfo();
const [ isExpanded, setIsExpanded ] = React.useState(false);
......@@ -116,9 +73,9 @@ const TxDetails = () => {
return (
<Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }}>
{ socketAlert && (
{ socketStatus && (
<GridItem colSpan={{ base: undefined, lg: 2 }} mb={ 2 }>
<Alert status="warning" as="a" href={ window.document.location.href }>{ socketAlert }</Alert>
<TxSocketAlert status={ socketStatus }/>
</GridItem>
) }
<DetailsInfoItem
......
......@@ -6,6 +6,7 @@ import React from 'react';
import type { InternalTransactionsResponse, TxInternalsType, InternalTransaction } from 'types/api/internalTransaction';
import { QueryKeys } from 'types/client/queries';
import { SECOND } from 'lib/consts';
import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile';
import { apos } from 'lib/html-entities';
......@@ -18,6 +19,9 @@ import TxInternalsSkeletonDesktop from 'ui/tx/internals/TxInternalsSkeletonDeskt
import TxInternalsSkeletonMobile from 'ui/tx/internals/TxInternalsSkeletonMobile';
import TxInternalsTable from 'ui/tx/internals/TxInternalsTable';
import type { Sort, SortField } from 'ui/tx/internals/utils';
import TxPendingAlert from 'ui/tx/TxPendingAlert';
import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const SORT_SEQUENCE: Record<SortField, Array<Sort | undefined>> = {
value: [ 'value-desc', 'value-asc', undefined ],
......@@ -72,11 +76,12 @@ const TxInternals = () => {
const [ filters, setFilters ] = React.useState<Array<TxInternalsType>>([]);
const [ searchTerm, setSearchTerm ] = React.useState<string>('');
const [ sort, setSort ] = React.useState<Sort>();
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isLoading, isError } = useQuery<unknown, unknown, InternalTransactionsResponse>(
[ QueryKeys.txInternals, router.query.id ],
async() => await fetch(`/node-api/transactions/${ router.query.id }/internal-transactions`),
{
enabled: Boolean(router.query.id),
enabled: Boolean(router.query.id) && Boolean(txInfo.data?.status),
},
);
......@@ -92,7 +97,11 @@ const TxInternals = () => {
};
}, []);
if (isLoading) {
if (!txInfo.isLoading && !txInfo.isError && !txInfo.data.status) {
return txInfo.socketStatus ? <TxSocketAlert status={ txInfo.socketStatus }/> : <TxPendingAlert/>;
}
if (isLoading || txInfo.isLoading) {
return (
<>
<Show below="lg"><TxInternalsSkeletonMobile/></Show>
......@@ -101,7 +110,7 @@ const TxInternals = () => {
);
}
if (isError) {
if (isError || txInfo.isError) {
return <DataFetchAlert/>;
}
......
......@@ -6,28 +6,37 @@ import React from 'react';
import type { LogsResponse } from 'types/api/log';
import { QueryKeys } from 'types/client/queries';
import { SECOND } from 'lib/consts';
import useFetch from 'lib/hooks/useFetch';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxLogItem from 'ui/tx/logs/TxLogItem';
import TxLogSkeleton from 'ui/tx/logs/TxLogSkeleton';
import TxPendingAlert from 'ui/tx/TxPendingAlert';
import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const TxLogs = () => {
const router = useRouter();
const fetch = useFetch();
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isLoading, isError } = useQuery<unknown, unknown, LogsResponse>(
[ QueryKeys.txLog, router.query.id ],
async() => await fetch(`/node-api/transactions/${ router.query.id }/logs`),
{
enabled: Boolean(router.query.id),
enabled: Boolean(router.query.id) && Boolean(txInfo.data?.status),
},
);
if (isError) {
if (!txInfo.isLoading && !txInfo.isError && !txInfo.data.status) {
return txInfo.socketStatus ? <TxSocketAlert status={ txInfo.socketStatus }/> : <TxPendingAlert/>;
}
if (isError || txInfo.isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
if (isLoading || txInfo.isLoading) {
return (
<Box>
<TxLogSkeleton/>
......
import { Alert, Spinner } from '@chakra-ui/react';
import React from 'react';
const TxPendingAlert = () => {
return (
<Alert>
<Spinner size="sm" mr={ 2 }/>
This transaction is pending confirmation.
</Alert>
);
};
export default TxPendingAlert;
......@@ -6,27 +6,36 @@ import React from 'react';
import type { RawTracesResponse } from 'types/api/rawTrace';
import { QueryKeys } from 'types/client/queries';
import { SECOND } from 'lib/consts';
import useFetch from 'lib/hooks/useFetch';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxPendingAlert from 'ui/tx/TxPendingAlert';
import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const TxRawTrace = () => {
const router = useRouter();
const fetch = useFetch();
const txInfo = useFetchTxInfo({ updateDelay: 5 * SECOND });
const { data, isLoading, isError } = useQuery<unknown, unknown, RawTracesResponse>(
[ QueryKeys.txRawTrace, router.query.id ],
async() => await fetch(`/node-api/transactions/${ router.query.id }/raw-trace`),
{
enabled: Boolean(router.query.id),
enabled: Boolean(router.query.id) && Boolean(txInfo.data?.status),
},
);
if (isError) {
if (!txInfo.isLoading && !txInfo.isError && !txInfo.data.status) {
return txInfo.socketStatus ? <TxSocketAlert status={ txInfo.socketStatus }/> : <TxPendingAlert/>;
}
if (isError || txInfo.isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
if (isLoading || txInfo.isLoading) {
return (
<>
<Flex justifyContent="end" mb={ 2 }>
......
import { Alert } from '@chakra-ui/react';
import React from 'react';
interface Props {
status: 'error' | 'close';
}
const TxSocketAlert = ({ status }: Props) => {
const text = status === 'close' ?
'Connection is lost. Please click here to update transaction info.' :
'An error has occurred while fetching transaction info. Please click here to update.';
return <Alert status="warning" as="a" href={ window.document.location.href }>{ text }</Alert>;
};
export default React.memo(TxSocketAlert);
import { Tr, Td, Tag, Icon, Box } from '@chakra-ui/react';
import { Tr, Td, Tag, Icon, Box, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
......@@ -19,12 +19,14 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit:
return (
<Tr alignItems="top">
<Td>
{ typeTitle && (
<Box w="126px" display="inline-block">
<Tag colorScheme="cyan" mr={ 5 }>{ typeTitle }</Tag>
</Box>
) }
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error }/>
<Flex rowGap={ 2 } flexWrap="wrap">
{ typeTitle && (
<Box w="126px" display="inline-block">
<Tag colorScheme="cyan" mr={ 5 }>{ typeTitle }</Tag>
</Box>
) }
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error }/>
</Flex>
</Td>
<Td>
<Address display="inline-flex" maxW="100%">
......
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { Transaction } from 'types/api/transaction';
import { QueryKeys } from 'types/client/queries';
import delay from 'lib/delay';
import useFetch from 'lib/hooks/useFetch';
import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage';
interface Params {
onTxStatusUpdate?: () => void;
updateDelay?: number;
}
type ReturnType = UseQueryResult<Transaction, unknown> & {
socketStatus: 'close' | 'error' | undefined;
}
export default function useFetchTxInfo({ onTxStatusUpdate, updateDelay }: Params | undefined = {}): ReturnType {
const router = useRouter();
const fetch = useFetch();
const queryClient = useQueryClient();
const [ socketStatus, setSocketStatus ] = React.useState<'close' | 'error'>();
const queryResult = useQuery<unknown, unknown, Transaction>(
[ QueryKeys.tx, router.query.id ],
async() => await fetch(`/node-api/transactions/${ router.query.id }`),
{
enabled: Boolean(router.query.id),
refetchOnMount: false,
},
);
const { data, isError, isLoading } = queryResult;
const handleStatusUpdateMessage: SocketMessage.TxStatusUpdate['handler'] = React.useCallback(async() => {
updateDelay && await delay(updateDelay);
queryClient.invalidateQueries({ queryKey: [ QueryKeys.tx, router.query.id ] });
onTxStatusUpdate?.();
}, [ onTxStatusUpdate, queryClient, router.query.id, updateDelay ]);
const handleSocketClose = React.useCallback(() => {
setSocketStatus('close');
}, []);
const handleSocketError = React.useCallback(() => {
setSocketStatus('error');
}, []);
const channel = useSocketChannel({
topic: `transactions:${ router.query.id }`,
onSocketClose: handleSocketClose,
onSocketError: handleSocketError,
isDisabled: isLoading || isError || data.status !== null,
});
useSocketMessage({
channel,
event: 'collated',
handler: handleStatusUpdateMessage,
});
return {
...queryResult,
socketStatus,
};
}
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