feat(explorer): add txs infinite scroll list (#1625)

This commit is contained in:
Elmar 2022-10-07 09:46:54 +01:00 committed by GitHub
parent 057322c4d7
commit ec47ee2930
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 189 additions and 209 deletions

View File

@ -6,6 +6,7 @@ NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https:
NX_VEGA_ENV=STAGNET3 NX_VEGA_ENV=STAGNET3
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_BLOCK_EXPLORER=
# App flags # App flags
NX_EXPLORER_ASSETS=1 NX_EXPLORER_ASSETS=1

View File

@ -7,3 +7,4 @@ NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=DEVNET NX_VEGA_ENV=DEVNET
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=

View File

@ -7,3 +7,4 @@ NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=MAINNET NX_VEGA_ENV=MAINNET
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=

View File

@ -7,3 +7,4 @@ NX_VEGA_URL=https://api.n01.stagnet3.vega.xyz/graphql
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=STAGNET3 NX_VEGA_ENV=STAGNET3
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=

View File

@ -1,6 +1,7 @@
# App configuration variables # App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz
NX_BLOCK_EXPLORER=https://n13.testnet.vega.xyz/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n07.testnet.vega.xyz/websocket NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n07.testnet.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'

View File

@ -5,3 +5,4 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
NX_VEGA_URL=http://localhost:3003/query NX_VEGA_URL=http://localhost:3003/query
NX_VEGA_ENV=CUSTOM NX_VEGA_ENV=CUSTOM
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=

View File

@ -1,92 +1,90 @@
import { TxsInfiniteListItem } from './txs-infinite-list-item'; import { TxsInfiniteListItem } from './txs-infinite-list-item';
import { render, screen, fireEvent, act } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
describe('Txs infinite list item', () => { describe('Txs infinite list item', () => {
it('should display "missing vital data" if "Type" data missing', () => { it('should display "missing vital data" if "type" data missing', () => {
render( render(
<TxsInfiniteListItem <TxsInfiniteListItem
// @ts-ignore testing deliberate failure type={undefined}
Type={undefined} submitter="test"
Command={'test'} hash=""
Sig={'test'} index={0}
PubKey={'test'} block="1"
Nonce={1}
TxHash={'test'}
/> />
); );
expect(screen.getByText('Missing vital data')).toBeInTheDocument(); expect(screen.getByText('Missing vital data')).toBeInTheDocument();
}); });
it('should display "missing vital data" if "Command" data missing', () => { it('should display "missing vital data" if "hash" data missing', () => {
render( render(
<TxsInfiniteListItem <TxsInfiniteListItem
Type={'test'} type="test"
// @ts-ignore testing deliberate failure submitter="test"
Command={undefined} hash={undefined}
Sig={'test'} index={0}
PubKey={'test'} block="1"
Nonce={1}
TxHash={'test'}
/> />
); );
expect(screen.getByText('Missing vital data')).toBeInTheDocument(); expect(screen.getByText('Missing vital data')).toBeInTheDocument();
}); });
it('should display "missing vital data" if "Pubkey" data missing', () => { it('should display "missing vital data" if "submitter" data missing', () => {
render( render(
<TxsInfiniteListItem <TxsInfiniteListItem
Type={'test'} type="test"
Command={'test'} submitter={undefined}
Sig={'test'} hash="test"
// @ts-ignore testing deliberate failure index={0}
PubKey={undefined} block="1"
Nonce={1}
TxHash={'test'}
/> />
); );
expect(screen.getByText('Missing vital data')).toBeInTheDocument(); expect(screen.getByText('Missing vital data')).toBeInTheDocument();
}); });
it('should display "missing vital data" if "TxHash" data missing', () => { it('should display "missing vital data" if "block" data missing', () => {
render( render(
<TxsInfiniteListItem <TxsInfiniteListItem
Type={'test'} type="test"
Command={'test'} submitter="test"
Sig={'test'} hash="test"
PubKey={'test'} index={0}
Nonce={1} block={undefined}
// @ts-ignore testing deliberate failure />
TxHash={undefined} );
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
});
it('should display "missing vital data" if "index" data missing', () => {
render(
<TxsInfiniteListItem
type="test"
submitter="test"
hash="test"
index={undefined}
block="1"
/> />
); );
expect(screen.getByText('Missing vital data')).toBeInTheDocument(); expect(screen.getByText('Missing vital data')).toBeInTheDocument();
}); });
it('renders data correctly', () => { it('renders data correctly', () => {
const testCommandData = JSON.stringify({
test: 'test of command data',
});
render( render(
<MemoryRouter> <MemoryRouter>
<TxsInfiniteListItem <TxsInfiniteListItem
Type={'testType'} type="testType"
Command={testCommandData} submitter="testPubKey"
Sig={'testSig'} hash="testTxHash"
PubKey={'testPubKey'} index={1}
Nonce={1} block="1"
TxHash={'testTxHash'}
/> />
</MemoryRouter> </MemoryRouter>
); );
expect(screen.getByTestId('tx-hash')).toHaveTextContent('testTxHash'); expect(screen.getByTestId('tx-hash')).toHaveTextContent('testTxHash');
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey'); expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
expect(screen.getByTestId('type')).toHaveTextContent('testType'); expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
const button = screen.getByTestId('command-details'); expect(screen.getByTestId('tx-block')).toHaveTextContent(
act(() => { 'Block 1 (index 1)'
fireEvent.click(button); );
});
expect(screen.getByText('"test of command data"')).toBeInTheDocument();
}); });
}); });

View File

@ -1,69 +1,60 @@
import React, { useState } from 'react'; import React from 'react';
import {
Dialog,
Icon,
Intent,
SyntaxHighlighter,
} from '@vegaprotocol/ui-toolkit';
import { TruncatedLink } from '../truncate/truncated-link'; import { TruncatedLink } from '../truncate/truncated-link';
import { Routes } from '../../routes/route-names'; import { Routes } from '../../routes/route-names';
import { TxOrderType } from './tx-order-type'; import { TxOrderType } from './tx-order-type';
import type { ChainExplorerTxResponse } from '../../routes/types/chain-explorer-response'; import type { BlockExplorerTransaction } from '../../routes/types/block-explorer-response';
import { toHex } from '../search/detect-search';
const TRUNCATE_LENGTH = 14; const TRUNCATE_LENGTH = 14;
export const TxsInfiniteListItem = ({ export const TxsInfiniteListItem = ({
TxHash, hash,
PubKey, submitter,
Type, type,
Command, block,
}: ChainExplorerTxResponse) => { index,
const [open, setOpen] = useState(false); }: Partial<BlockExplorerTransaction>) => {
if (
if (!TxHash || !PubKey || !Type || !Command) { !hash ||
!submitter ||
!type ||
block === undefined ||
index === undefined
) {
return <div>Missing vital data</div>; return <div>Missing vital data</div>;
} }
return ( return (
<div <div
data-testid="transaction-row" data-testid="transaction-row"
className="grid grid-cols-[repeat(2,_1fr)_240px] gap-12 w-full border-t border-neutral-600 dark:border-neutral-800 py-8 txs-infinite-list-item" className="grid grid-flow-col auto-cols-auto border-t border-neutral-600 dark:border-neutral-800 py-2 txs-infinite-list-item"
> >
<div className="whitespace-nowrap overflow-scroll" data-testid="tx-hash"> <div className="whitespace-nowrap" data-testid="tx-type">
<TxOrderType orderType={type} />
</div>
<div className="whitespace-nowrap" data-testid="pub-key">
<TruncatedLink <TruncatedLink
to={`/${Routes.TX}/${TxHash}`} to={`/${Routes.PARTIES}/${submitter}`}
text={TxHash} text={submitter}
startChars={TRUNCATE_LENGTH} startChars={TRUNCATE_LENGTH}
endChars={TRUNCATE_LENGTH} endChars={TRUNCATE_LENGTH}
/> />
</div> </div>
<div className="whitespace-nowrap overflow-scroll" data-testid="pub-key"> <div className="whitespace-nowrap" data-testid="tx-hash">
<TruncatedLink <TruncatedLink
to={`/${Routes.PARTIES}/${PubKey}`} to={`/${Routes.TX}/${toHex(hash)}`}
text={PubKey} text={hash}
startChars={TRUNCATE_LENGTH} startChars={TRUNCATE_LENGTH}
endChars={TRUNCATE_LENGTH} endChars={TRUNCATE_LENGTH}
/> />
</div> </div>
<div <div className="whitespace-nowrap" data-testid="tx-block">
className="flex justify-between whitespace-nowrap overflow-scroll" <TruncatedLink
data-testid="type" to={`/${Routes.BLOCKS}/${block}`}
> text={`Block ${block} (index ${index})`}
<TxOrderType orderType={Type} /> startChars={TRUNCATE_LENGTH}
<button endChars={TRUNCATE_LENGTH}
title="More details" />
onClick={() => setOpen(true)}
data-testid="command-details"
>
<Icon name="search-template" />
</button>
<Dialog
open={open}
onChange={(isOpen) => setOpen(false)}
intent={Intent.None}
>
<SyntaxHighlighter data={JSON.parse(Command)} />
</Dialog>
</div> </div>
</div> </div>
); );

View File

@ -1,18 +1,34 @@
import { TxsInfiniteList } from './txs-infinite-list'; import { TxsInfiniteList } from './txs-infinite-list';
import { render, screen, fireEvent, act } from '@testing-library/react'; import { render, screen, fireEvent, act } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import type { BlockExplorerTransaction } from '../../routes/types/block-explorer-response';
const generateTxs = (number: number) => { const generateTxs = (number: number): BlockExplorerTransaction[] => {
return Array.from(Array(number)).map((_) => ({ return Array.from(Array(number)).map((_) => ({
Type: 'ChainEvent', block: '87901',
Command: index: 2,
'{"txId":"0xc8941ac4ea989988cb8f72e8fdab2e2009376fd17619491439d36b519d27bc93","nonce":"1494","stakingEvent":{"index":"263","block":"14805346","stakeDeposited":{"ethereumAddress":"0x2e5fe63e5d49c26998cf4bfa9b64de1cf9ae7ef2","vegaPublicKey":"657c2a8a5867c43c831e24820b7544e2fdcc1cf610cfe0ece940fe78137400fd","amount":"38471116086510047870875","blockTime":"1652968806"}}}', hash: '0F8B98DA0923A50786B852D9CA11E051CACC4C733E1DB93D535C7D81DBD10F6F',
Sig: 'fe7624ab742c492cf1e667e79de4777992aca8e093c8707e1f22685c3125c6082cd21b85cd966a61ad4ca0cca2f8bed3082565caa5915bc3b2f78c1ae35cac0b', submitter:
PubKey: '4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964',
'0x7d69327393cdfaaae50e5e215feca65273eafabfb38f32b8124e66298af346d5', type: 'Submit Order',
Nonce: 18296387398179254000, code: 0,
TxHash: cursor: '87901.2',
'0x9C753FA6325F7A40D9C4FA5C25E24476C54613E12B1FA2DD841E3BB00D088B77', command: {
nonce: '4214037379192575529',
blockHeight: '87898',
orderSubmission: {
marketId:
'b4d0a070f5cc73a7d53b23d6f63f8cb52e937ed65d2469a3af4cc1e80e155fcf',
price: '14525946',
size: '54',
side: 'SIDE_SELL',
timeInForce: 'TIME_IN_FORCE_GTT',
expiresAt: '1664966445481288736',
type: 'TYPE_LIMIT',
reference: 'traderbot',
peggedOrder: null,
},
},
})); }));
}; };
@ -41,7 +57,9 @@ describe('Txs infinite list', () => {
error={Error('test error!')} error={Error('test error!')}
/> />
); );
expect(screen.getByText('Error: test error!')).toBeInTheDocument(); expect(
screen.getByText('Cannot fetch transaction: Error: test error!')
).toBeInTheDocument();
}); });
it('item renders data of n length into list of n length', () => { it('item renders data of n length into list of n length', () => {

View File

@ -3,19 +3,19 @@ import { FixedSizeList as List } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader'; import InfiniteLoader from 'react-window-infinite-loader';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { TxsInfiniteListItem } from './txs-infinite-list-item'; import { TxsInfiniteListItem } from './txs-infinite-list-item';
import type { ChainExplorerTxResponse } from '../../routes/types/chain-explorer-response'; import type { BlockExplorerTransaction } from '../../routes/types/block-explorer-response';
interface TxsInfiniteListProps { interface TxsInfiniteListProps {
hasMoreTxs: boolean; hasMoreTxs: boolean;
areTxsLoading: boolean | undefined; areTxsLoading: boolean | undefined;
txs: ChainExplorerTxResponse[] | undefined; txs: BlockExplorerTransaction[] | undefined;
loadMoreTxs: () => void; loadMoreTxs: () => void;
error: Error | undefined; error: Error | undefined;
className?: string; className?: string;
} }
interface ItemProps { interface ItemProps {
index: ChainExplorerTxResponse; index: BlockExplorerTransaction;
style: React.CSSProperties; style: React.CSSProperties;
isLoading: boolean; isLoading: boolean;
error: Error | undefined; error: Error | undefined;
@ -27,19 +27,19 @@ const NOOP = () => {};
const Item = ({ index, style, isLoading, error }: ItemProps) => { const Item = ({ index, style, isLoading, error }: ItemProps) => {
let content; let content;
if (error) { if (error) {
content = t(`${error}`); content = t(`Cannot fetch transaction: ${error}`);
} else if (isLoading) { } else if (isLoading) {
content = t('Loading...'); content = t('Loading...');
} else { } else {
const { TxHash, PubKey, Type, Command, Sig, Nonce } = index; const { hash, submitter, type, command, block, index: blockIndex } = index;
content = ( content = (
<TxsInfiniteListItem <TxsInfiniteListItem
Type={Type} type={type}
Command={Command} command={command}
Sig={Sig} submitter={submitter}
PubKey={PubKey} hash={hash}
Nonce={Nonce} block={block}
TxHash={TxHash} index={blockIndex}
/> />
); );
} }
@ -71,10 +71,11 @@ export const TxsInfiniteList = ({
return ( return (
<div className={className} data-testid="transactions-list"> <div className={className} data-testid="transactions-list">
<div className="grid grid-cols-[repeat(2,_1fr)_240px] gap-12 w-full mb-8"> <div className="grid grid-flow-col auto-cols-auto w-full mb-8">
<div className="text-lg font-bold">Txn hash</div>
<div className="text-lg font-bold">Party</div>
<div className="text-lg font-bold pl-2">Type</div> <div className="text-lg font-bold pl-2">Type</div>
<div className="text-lg font-bold">Submitted By</div>
<div className="text-lg font-bold">Transaction ID</div>
<div className="text-lg font-bold">Block</div>
</div> </div>
<div data-testid="infinite-scroll-wrapper"> <div data-testid="infinite-scroll-wrapper">
<InfiniteLoader <InfiniteLoader

View File

@ -13,6 +13,7 @@ export const ENV = {
dsn: windowOrDefault('NX_EXPLORER_SENTRY_DSN'), dsn: windowOrDefault('NX_EXPLORER_SENTRY_DSN'),
dataSources: { dataSources: {
chainExplorerUrl: windowOrDefault('NX_CHAIN_EXPLORER_URL'), chainExplorerUrl: windowOrDefault('NX_CHAIN_EXPLORER_URL'),
blockExplorerUrl: windowOrDefault('NX_BLOCK_EXPLORER'),
tendermintUrl: windowOrDefault('NX_TENDERMINT_URL'), tendermintUrl: windowOrDefault('NX_TENDERMINT_URL'),
tendermintWebsocketUrl: windowOrDefault('NX_TENDERMINT_WEBSOCKET_URL'), tendermintWebsocketUrl: windowOrDefault('NX_TENDERMINT_WEBSOCKET_URL'),
}, },

View File

@ -13,7 +13,7 @@ import Genesis from './genesis';
import { Block } from './blocks/id'; import { Block } from './blocks/id';
import { Blocks } from './blocks/home'; import { Blocks } from './blocks/home';
import { Tx } from './txs/id'; import { Tx } from './txs/id';
import { TxsHome, TxsHomeFallback } from './txs/home'; import { TxsList } from './txs/home';
import { PendingTxs } from './pending'; import { PendingTxs } from './pending';
import flags from '../config/flags'; import flags from '../config/flags';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
@ -141,7 +141,7 @@ const routerConfig = [
}, },
{ {
index: true, index: true,
element: flags.txsList ? <TxsHome /> : <TxsHomeFallback />, element: <TxsList />,
}, },
], ],
}, },

View File

@ -1,86 +1,73 @@
import { DATA_SOURCES } from '../../../config'; import { DATA_SOURCES } from '../../../config';
import { useCallback, useState, useMemo } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { t, useFetch } from '@vegaprotocol/react-helpers'; import { t, useFetch } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../../components/route-title'; import { RouteTitle } from '../../../components/route-title';
import { BlocksRefetch } from '../../../components/blocks'; import { BlocksRefetch } from '../../../components/blocks';
import { JumpToBlock } from '../../../components/jump-to-block';
import { TxsInfiniteList } from '../../../components/txs'; import { TxsInfiniteList } from '../../../components/txs';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import type {
import type { ChainExplorerTxResponse } from '../../types/chain-explorer-response'; BlockExplorerTransaction,
import type { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain-response'; BlockExplorerTransactions,
} from '../../../routes/types/block-explorer-response';
interface TxsProps {
latestBlockHeight: string;
}
interface TxsStateProps { interface TxsStateProps {
txsData: ChainExplorerTxResponse[]; txsData: BlockExplorerTransaction[];
hasMoreTxs: boolean; hasMoreTxs: boolean;
nextPage: number; lastCursor: string;
} }
const Txs = ({ latestBlockHeight }: TxsProps) => { const BE_TXS_PER_REQUEST = 100;
const [{ txsData, hasMoreTxs, nextPage }, setTxsState] =
export const TxsList = () => {
const [{ txsData, hasMoreTxs, lastCursor }, setTxsState] =
useState<TxsStateProps>({ useState<TxsStateProps>({
txsData: [], txsData: [],
hasMoreTxs: true, hasMoreTxs: true,
nextPage: 1, lastCursor: '',
}); });
const reusedBodyParams = useMemo(
() => ({
node_url: DATA_SOURCES.tendermintUrl,
transaction_height: parseInt(latestBlockHeight),
page_size: 30,
}),
[latestBlockHeight]
);
const { const {
state: { error, loading }, state: { data, error, loading },
refetch, refetch,
} = useFetch( } = useFetch<BlockExplorerTransactions>(
DATA_SOURCES.chainExplorerUrl, `${DATA_SOURCES.blockExplorerUrl}/transactions?` +
{ new URLSearchParams({
method: 'POST', limit: BE_TXS_PER_REQUEST.toString(10),
body: JSON.stringify(reusedBodyParams), }),
}, {},
false false
); );
const loadTxs = useCallback(async () => { useEffect(() => {
const data = await refetch( if (data?.transactions?.length) {
undefined,
JSON.stringify({
...reusedBodyParams,
page_number: nextPage,
})
);
if (data) {
setTxsState((prev) => ({ setTxsState((prev) => ({
...prev, txsData: [...prev.txsData, ...data.transactions],
nextPage: prev.nextPage + 1,
hasMoreTxs: true, hasMoreTxs: true,
txsData: [...prev.txsData, ...(data as ChainExplorerTxResponse[])], lastCursor:
data.transactions[data.transactions.length - 1].cursor || '',
})); }));
} }
}, [nextPage, refetch, reusedBodyParams]); }, [data?.transactions]);
const loadTxs = useCallback(() => {
return refetch({
limit: BE_TXS_PER_REQUEST,
before: lastCursor,
});
}, [lastCursor, refetch]);
const refreshTxs = useCallback(async () => {
setTxsState((prev) => ({
...prev,
lastCursor: '',
hasMoreTxs: true,
txsData: [],
}));
}, [setTxsState]);
return ( return (
<section> <section>
<RouteTitle>{t('Transactions')}</RouteTitle> <RouteTitle>{t('Transactions')}</RouteTitle>
<BlocksRefetch <BlocksRefetch refetch={refreshTxs} />
refetch={() =>
refetch(
undefined,
JSON.stringify({
...reusedBodyParams,
page_number: 1,
})
)
}
/>
<TxsInfiniteList <TxsInfiniteList
hasMoreTxs={hasMoreTxs} hasMoreTxs={hasMoreTxs}
areTxsLoading={loading} areTxsLoading={loading}
@ -89,42 +76,6 @@ const Txs = ({ latestBlockHeight }: TxsProps) => {
error={error} error={error}
className="mb-28" className="mb-28"
/> />
<JumpToBlock />
</section> </section>
); );
}; };
export const TxsHome = () => {
const {
state: { data, error, loading },
} = useFetch<TendermintBlockchainResponse>(
`${DATA_SOURCES.tendermintUrl}/blockchain`
);
return (
<AsyncRenderer
loading={!!loading}
loadingMessage={t('Getting latest block height...')}
error={error}
data={data}
noDataMessage={t('Could not get latest block height')}
render={(data) => (
<Txs
latestBlockHeight={
data?.result?.block_metas?.[0]?.header?.height || ''
}
/>
)}
/>
);
};
export const TxsHomeFallback = () => (
<>
<RouteTitle>{t('Transactions')}</RouteTitle>
<div>
The transactions list is currently disabled. Please use the search bar to
discover transaction data
</div>
</>
);

View File

@ -0,0 +1,14 @@
export interface BlockExplorerTransaction {
block: string;
index: number;
hash: string;
submitter: string;
type: string;
code: number;
cursor: string;
command: Record<string, unknown>;
}
export interface BlockExplorerTransactions {
transactions: BlockExplorerTransaction[];
}