diff --git a/src/components/Blocks/BlockCard.tsx b/src/components/Blocks/BlockCard.tsx index 0342c14..5725531 100644 --- a/src/components/Blocks/BlockCard.tsx +++ b/src/components/Blocks/BlockCard.tsx @@ -1,7 +1,9 @@ -import { Box, Card, CardBody, Flex, Link, Text } from '@chakra-ui/react' +import { Box, Card, CardBody, Flex, Link, HStack, Icon, Text } from '@chakra-ui/react' import { BlockError, BlockNotFoundError } from '@vocdoni/extended-sdk' import { IChainBlockInfoResponse } from '@vocdoni/sdk' import { Trans, useTranslation } from 'react-i18next' +import { BiTransferAlt } from 'react-icons/bi' + import { generatePath, Link as RouterLink } from 'react-router-dom' import { ReducedTextAndCopy } from '~components/Layout/CopyButton' import { RoutePath } from '~constants' @@ -9,20 +11,43 @@ import { useDateFns } from '~i18n/use-date-fns' export const BlockCard = ({ block }: { block: IChainBlockInfoResponse | BlockError }) => { if (block instanceof BlockError) return - return + return ( + + ) } -const BlockInfoCard = ({ height, time, proposer }: { height: number; time: string; proposer: string }) => { +const BlockInfoCard = ({ + height, + time, + proposer, + txn, +}: { + height: number + time: string + proposer: string + txn: number +}) => { const date = new Date(time) const { formatDistance } = useDateFns() return ( - + # {height} + + + + {txn} + + {formatDistance(date, new Date())} diff --git a/src/components/Blocks/Detail.tsx b/src/components/Blocks/Detail.tsx index fc4b60f..b3f7dbc 100644 --- a/src/components/Blocks/Detail.tsx +++ b/src/components/Blocks/Detail.tsx @@ -1,15 +1,16 @@ import { Flex, Heading, IconButton, Tab, TabList, TabPanel, TabPanels, Text, VStack } from '@chakra-ui/react' import { ensure0x, IChainBlockInfoResponse } from '@vocdoni/sdk' import { Trans, useTranslation } from 'react-i18next' -import { RawContentBox } from '~components/Layout/ShowRawButton' -import { useDateFns } from '~i18n/use-date-fns' import { GrNext, GrPrevious } from 'react-icons/gr' -import { RefreshIntervalBlocks, RoutePath } from '~constants' -import { useBlocksHeight } from '~queries/blocks' import { generatePath, Link as RouterLink } from 'react-router-dom' -import { DetailsGrid, GridItemProps } from '~components/Layout/DetailsGrid' import { ReducedTextAndCopy } from '~components/Layout/CopyButton' +import { DetailsGrid, GridItemProps } from '~components/Layout/DetailsGrid' import { QueryParamsTabs } from '~components/Layout/QueryParamsTabs' +import { RawContentBox } from '~components/Layout/ShowRawButton' +import { BlockTransactionsList } from '~components/Transactions/TransactionList' +import { RefreshIntervalBlocks, RoutePath } from '~constants' +import { useDateFns } from '~i18n/use-date-fns' +import { useBlocksHeight } from '~queries/blocks' const HeightNavigator = ({ height }: { height: number }) => { const { data, isLoading } = useBlocksHeight({ @@ -27,7 +28,7 @@ const HeightNavigator = ({ height }: { height: number }) => { {height >= 1 && ( } size={'xs'} @@ -36,7 +37,7 @@ const HeightNavigator = ({ height }: { height: number }) => { {height < data && ( } size={'xs'} @@ -131,6 +132,9 @@ export const BlockDetail = ({ block }: { block: IChainBlockInfoResponse }) => { Details + + Transactions + Raw @@ -139,6 +143,9 @@ export const BlockDetail = ({ block }: { block: IChainBlockInfoResponse }) => { + + + diff --git a/src/components/Envelope/Detail.tsx b/src/components/Envelope/Detail.tsx index e8e12fe..87328f1 100644 --- a/src/components/Envelope/Detail.tsx +++ b/src/components/Envelope/Detail.tsx @@ -1,3 +1,4 @@ +import voteImage from '/images/vocdoni-vote.png' import { Flex, Heading, Image, Link, Text } from '@chakra-ui/react' import { IVoteInfoResponse } from '@vocdoni/sdk' import { formatDistance } from 'date-fns' @@ -6,7 +7,6 @@ import { generatePath, Link as RouterLink } from 'react-router-dom' import { CopyButton } from '~components/Layout/CopyButton' import ShowRawButton from '~components/Layout/ShowRawButton' import { RoutePath } from '~constants' -import voteImage from '/images/vocdoni-vote.png' const EnvelopeDetail = (envelope: IVoteInfoResponse) => { const { t } = useTranslation() @@ -68,7 +68,10 @@ const EnvelopeDetail = (envelope: IVoteInfoResponse) => { i18nKey={'envelopes.committed_in_block'} components={{ a: ( - + ), }} values={{ block: envelope.blockHeight }} diff --git a/src/components/Process/Detail.tsx b/src/components/Process/Detail.tsx index e278980..452f571 100644 --- a/src/components/Process/Detail.tsx +++ b/src/components/Process/Detail.tsx @@ -35,6 +35,7 @@ import { import { Trans, useTranslation } from 'react-i18next' import { BiEnvelope } from 'react-icons/bi' import { generatePath, Link as RouterLink } from 'react-router-dom' +import { ReducedTextAndCopy } from '~components/Layout/CopyButton' import { HeroHeaderLayout } from '~components/Layout/HeroHeaderLayout' import { LoadingCards } from '~components/Layout/Loading' import { RawContentBox } from '~components/Layout/ShowRawButton' @@ -46,7 +47,6 @@ import InvalidElection from '~components/Process/InvalidElection' import { FallbackHeaderImg, RoutePath } from '~constants' import { useElectionKeys, useElectionVotesList } from '~queries/processes' import { ucfirst } from '~utils/strings' -import { ReducedTextAndCopy } from '~components/Layout/CopyButton' const Detail = () => { const { election } = useElection() @@ -279,7 +279,10 @@ const EnvelopeCard = ({ envelope, count }: { envelope: IElectionVote; count: num - + Block {{ height: envelope.blockHeight }} diff --git a/src/components/Transactions/TransactionList.tsx b/src/components/Transactions/TransactionList.tsx index 5ded0fe..a3e28aa 100644 --- a/src/components/Transactions/TransactionList.tsx +++ b/src/components/Transactions/TransactionList.tsx @@ -1,6 +1,8 @@ +import { Text } from '@chakra-ui/react' import { keepPreviousData } from '@tanstack/react-query' +import { IBlockTransactionsResponse, IChainTxListResponse } from '@vocdoni/sdk' import { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' import { generatePath, useNavigate, useParams } from 'react-router-dom' import { PopoverInputSearch } from '~components/Layout/Inputs' import { LoadingCards } from '~components/Layout/Loading' @@ -9,6 +11,7 @@ import { RoutedPaginationProvider } from '~components/Pagination/PaginationProvi import { RoutedPagination } from '~components/Pagination/RoutedPagination' import { TransactionCard } from '~components/Transactions/TransactionCard' import { PaginationItemsPerPage, RoutePath } from '~constants' +import { useBlockTransactions } from '~queries/blocks' import { useTransactionList, useTransactionsCount } from '~queries/transactions' import { retryUnlessNotFound } from '~utils/queries' @@ -65,6 +68,50 @@ export const PaginatedTransactionList = () => { const isLoading = isLoadingCount || isLoadingTx + return +} + +/** + * Get transaction list by block height + * @constructor + */ +export const BlockTransactionsList = ({ blockHeight, totalTxs }: { blockHeight: number; totalTxs: number }) => { + const totalPages = Math.ceil(totalTxs / PaginationItemsPerPage) + const { page }: { page?: number } = useParams() + const currentPage = page && page > 0 ? Number(page - 1) : 0 + + const { data, isLoading, isError, error } = useBlockTransactions({ + blockHeight, + page: currentPage, + placeholderData: keepPreviousData, + retry: retryUnlessNotFound, + enabled: totalTxs > 0, + }) + + if (totalTxs <= 0) { + return ( + + There are no transactions. + + ) + } + + return +} + +const TransactionsList = ({ + isLoading, + data, + isError, + error, + totalPages, +}: { + isLoading: boolean + data: IChainTxListResponse | IBlockTransactionsResponse | undefined + isError: boolean + error: Error | null + totalPages: number +}) => { return ( <> {isLoading && } @@ -72,7 +119,11 @@ export const PaginatedTransactionList = () => { {data && data.transactions.length > 0 && ( {data.transactions.map((tx, i) => ( - + ))} diff --git a/src/components/Transactions/TxDetails/TxDetails.tsx b/src/components/Transactions/TxDetails/TxDetails.tsx index 285534d..2ef3076 100644 --- a/src/components/Transactions/TxDetails/TxDetails.tsx +++ b/src/components/Transactions/TxDetails/TxDetails.tsx @@ -58,7 +58,7 @@ export const TxDetailsGrid = (tx: Tx) => { children: ( {blockHeight} diff --git a/src/constants.ts b/src/constants.ts index 2886dc5..26067b5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -20,7 +20,7 @@ export const PaginationItemsPerPage = 10 // route paths export enum RoutePath { Base = '/', - Block = '/block/:height', + Block = '/block/:height/:page?', BlocksList = '/blocks/:page?', Envelope = '/envelope/:verifier', Organization = '/organization/:pid/:page?', diff --git a/src/queries/blocks.ts b/src/queries/blocks.ts index e92cd52..0801e7b 100644 --- a/src/queries/blocks.ts +++ b/src/queries/blocks.ts @@ -1,7 +1,7 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' import { BlockError, ExtendedSDKClient } from '@vocdoni/extended-sdk' import { useClient } from '@vocdoni/react-providers' -import { IChainBlockInfoResponse } from '@vocdoni/sdk' +import { IBlockTransactionsResponse, IChainBlockInfoResponse } from '@vocdoni/sdk' import { useChainInfo, useChainInfoOptions } from '~queries/stats' export const useBlockList = ({ @@ -26,3 +26,20 @@ export const useBlocksHeight = (options?: useChainInfoOptions) => { const height = data?.height return { data: height, ...rest } } + +export const useBlockTransactions = ({ + blockHeight, + page, + ...options +}: { + blockHeight: number + page: number +} & Omit, 'queryKey'>) => { + const { client } = useClient() + + return useQuery({ + queryKey: ['blocks', 'list', 'transactions', blockHeight, page], + queryFn: () => client.blockTransactions(blockHeight, page), + ...options, + }) +} diff --git a/yarn.lock b/yarn.lock index bc8539d..13ca0f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1977,10 +1977,10 @@ dependencies: "@vocdoni/react-providers" "~0.4.4" -"@vocdoni/extended-sdk@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@vocdoni/extended-sdk/-/extended-sdk-0.0.3.tgz#d9dd2240493650cbac9c258a138bab91111a9a37" - integrity sha512-5izKqahI6b9qYY0nB4MzKwwkL/aIMNsdSoPfP71bHvM7YPuBw83uAe6ffv6KZLI2vUZK7zCKSScYlnklW+WTqg== +"@vocdoni/extended-sdk@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@vocdoni/extended-sdk/-/extended-sdk-0.0.4.tgz#5c53ae926619b6228c1ca24ba6cc370d48d3d772" + integrity sha512-quKdccVBilFuSy8WUPBNt1XYMPcPkxYpjeeDGS6yOKeKcb0FVT6U6H+gqfq5agZHBmtavpPWarfplJ4Ky1c6zA== "@vocdoni/proto@1.15.8": version "1.15.8"