Skip to content

Commit

Permalink
Merge pull request #1256 from oasisprotocol/mz/consensusTxList
Browse files Browse the repository at this point in the history
Create Consensus transactions list components
  • Loading branch information
buberdds authored Feb 14, 2024
2 parents 634abcc + 0b90380 commit db64a37
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 1 deletion.
1 change: 1 addition & 0 deletions .changelog/1256.trivial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Consensus transactions list page and dashboard transactions card
148 changes: 148 additions & 0 deletions src/app/components/Transactions/ConsensusTransactions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'
import { Transaction } from '../../../oasis-nexus/api'
import { Table, TableCellAlign, TableColProps } from '../../components/Table'
import { RoundedBalance } from '../../components/RoundedBalance'
import { trimLongString } from '../../utils/trimLongString'
import { TablePaginationProps } from '../Table/TablePagination'
import { BlockLink } from '../Blocks/BlockLink'
import { AccountLink } from '../Account/AccountLink'
import { StatusIcon } from '../StatusIcon'
import { Age } from '../Age'
import { TransactionLink } from './TransactionLink'

type TableConsensusTransaction = Transaction & {
markAsNew?: boolean
}

export type TableConsensusTransactionList = {
transactions: TableConsensusTransaction[]
total_count: number
is_total_count_clipped: boolean
}

type ConsensusTransactionsProps = {
transactions?: TableConsensusTransaction[]
ownAddress?: string
isLoading: boolean
limit: number
pagination: false | TablePaginationProps
verbose?: boolean
}

export const ConsensusTransactions: FC<ConsensusTransactionsProps> = ({
isLoading,
limit,
pagination,
transactions,
ownAddress,
verbose = true,
}) => {
const { t } = useTranslation()

const tableColumns: TableColProps[] = [
{ key: 'status', content: t('common.status') },
{ key: 'hash', content: t('common.hash') },
{ key: 'block', content: t('common.block') },
{ key: 'age', content: t('common.age'), align: TableCellAlign.Right },
...(verbose
? [
{ key: 'type', content: t('common.type'), align: TableCellAlign.Center },
{ key: 'from', content: t('common.from'), width: '150px' },
{ key: 'to', content: t('common.to'), width: '150px' },
{ key: 'txnFee', content: t('common.transactionFee'), align: TableCellAlign.Right, width: '250px' },
{ key: 'value', align: TableCellAlign.Right, content: t('common.value'), width: '250px' },
]
: []),
]
const tableRows = transactions?.map(transaction => ({
key: transaction.hash,
data: [
{
content: <StatusIcon success={transaction.success} error={transaction.error} />,
key: 'success',
},
{
content: <TransactionLink scope={transaction} alwaysTrim={true} hash={transaction.hash} />,
key: 'hash',
},
{
content: <BlockLink scope={transaction} height={transaction.block} />,
key: 'round',
},
{
align: TableCellAlign.Right,
content: <Age sinceTimestamp={transaction.timestamp} />,
key: 'timestamp',
},
...(verbose
? [
{
align: TableCellAlign.Center,
// TODO: needs icons for ConsensusTxMethod
content: <>-</>,
key: 'type',
},
{
align: TableCellAlign.Right,
content: (
<Box
sx={{
display: 'flex',
alignItems: 'center',
position: 'relative',
pr: 3,
}}
>
{!!ownAddress && transaction.sender === ownAddress ? (
<Typography
variant="mono"
component="span"
sx={{
fontWeight: 700,
}}
>
{trimLongString(transaction.sender)}
</Typography>
) : (
<AccountLink scope={transaction} address={transaction.sender} alwaysTrim={true} />
)}
</Box>
),
key: 'from',
},
{
// TODO: show recipients address when props is added to API
content: <>-</>,
key: 'to',
},
{
align: TableCellAlign.Right,
content: <RoundedBalance value={transaction.fee} ticker={transaction.ticker} />,
key: 'fee_amount',
},
{
align: TableCellAlign.Right,
// TODO: show RoundedBalance when API returns amount prop
content: <>-</>,
key: 'value',
},
]
: []),
],
highlight: transaction.markAsNew,
}))

return (
<Table
columns={tableColumns}
rows={tableRows}
rowsNumber={limit}
name={t('transactions.latest')}
isLoading={isLoading}
pagination={pagination}
/>
)
}
1 change: 1 addition & 0 deletions src/app/components/Transactions/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './RuntimeTransactions'
export * from './ConsensusTransactions'
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import { Link as RouterLink } from 'react-router-dom'
import Link from '@mui/material/Link'
import { useGetConsensusTransactions } from '../../../oasis-nexus/api'
import { SearchScope } from '../../../types/searchScope'
import { ConsensusTransactions } from '../../components/Transactions'
import { NUMBER_OF_ITEMS_ON_DASHBOARD as limit } from '../../config'
import { RouteUtils } from '../../utils/route-utils'
import { useScreenSize } from '../../hooks/useScreensize'

export const LatestConsensusTransactions: FC<{ scope: SearchScope }> = ({ scope }) => {
const { isTablet } = useScreenSize()
const { t } = useTranslation()
const { network } = scope

const transactionsQuery = useGetConsensusTransactions(
network,
{ limit },
{
query: {
cacheTime: 0,
},
},
)

return (
<Card>
<CardHeader
disableTypography
component="h3"
title={t('transactions.latest')}
action={
<Link component={RouterLink} to={RouteUtils.getLatestTransactionsRoute(scope)}>
{t('common.viewAll')}
</Link>
}
/>
<CardContent>
<ConsensusTransactions
transactions={transactionsQuery.data?.data.transactions}
isLoading={transactionsQuery.isLoading}
limit={limit}
pagination={false}
verbose={!isTablet}
/>
</CardContent>
</Card>
)
}
2 changes: 2 additions & 0 deletions src/app/pages/ConsensusDashboardPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ValidatorsCard } from './Validators'
import { ConsensusSnapshot } from './ConsensusSnapshot'
import { LatestConsensusBlocks } from './LatestConsensusBlocks'
import { AccountsCard } from './AccountsCard'
import { LatestConsensusTransactions } from './LatestConsensusTransactions'

export const ConsensusDashboardPage: FC = () => {
const { isMobile } = useScreenSize()
Expand All @@ -32,6 +33,7 @@ export const ConsensusDashboardPage: FC = () => {
</Grid>
<ValidatorsCard scope={scope} />
<AccountsCard scope={scope} />
<LatestConsensusTransactions scope={scope} />
<NetworkProposalsCard scope={scope} />
<TransactionsStats scope={scope} />
<LearningMaterials />
Expand Down
102 changes: 102 additions & 0 deletions src/app/pages/ConsensusTransactionsPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Divider from '@mui/material/Divider'
import { useScreenSize } from '../../hooks/useScreensize'
import { PageLayout } from '../../components/PageLayout'
import { SubPageCard } from '../../components/SubPageCard'
import { TableConsensusTransactionList, ConsensusTransactions } from '../../components/Transactions'
import { useGetConsensusTransactions } from '../../../oasis-nexus/api'
import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE as limit, REFETCH_INTERVAL } from '../../config'
import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination'
import { AxiosResponse } from 'axios'
import { AppErrors } from '../../../types/errors'
import { LoadMoreButton } from '../../components/LoadMoreButton'
import { TableLayout, TableLayoutButton } from '../../components/TableLayoutButton'
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
import { VerticalList } from '../../components/VerticalList'

export const ConsensusTransactionsPage: FC = () => {
const [tableView, setTableView] = useState<TableLayout>(TableLayout.Horizontal)
const { t } = useTranslation()
const { isMobile } = useScreenSize()
const pagination = useSearchParamsPagination('page')
const offset = (pagination.selectedPage - 1) * limit
const scope = useRequiredScopeParam()

useEffect(() => {
if (!isMobile) {
setTableView(TableLayout.Horizontal)
}
}, [isMobile, setTableView])

const transactionsQuery = useGetConsensusTransactions<AxiosResponse<TableConsensusTransactionList>>(
scope.network,
{
limit: tableView === TableLayout.Vertical ? offset + limit : limit,
offset: tableView === TableLayout.Vertical ? 0 : offset,
},
{
query: {
refetchInterval: REFETCH_INTERVAL,
structuralSharing: (previousState, nextState) => {
const oldTxHashes = new Set(previousState?.data.transactions.map(tx => tx.hash))
return {
...nextState,
data: {
...nextState.data,
transactions: nextState.data.transactions.map(tx => {
return {
...tx,
markAsNew: previousState ? !oldTxHashes.has(tx.hash) : false,
}
}),
},
}
},
keepPreviousData: tableView === TableLayout.Vertical,
},
},
)

const { isLoading, isFetched, data } = transactionsQuery

const transactions = data?.data.transactions

if (isFetched && pagination.selectedPage > 1 && !transactions?.length) {
throw AppErrors.PageDoesNotExist
}

return (
<PageLayout
mobileFooterAction={
tableView === TableLayout.Vertical && <LoadMoreButton pagination={pagination} isLoading={isLoading} />
}
>
{!isMobile && <Divider variant="layout" />}
<SubPageCard
title={t('transactions.latest')}
action={isMobile && <TableLayoutButton tableView={tableView} setTableView={setTableView} />}
noPadding={tableView === TableLayout.Vertical}
>
{tableView === TableLayout.Horizontal && (
<ConsensusTransactions
transactions={data?.data.transactions}
isLoading={isLoading}
limit={limit}
pagination={{
selectedPage: pagination.selectedPage,
linkToPage: pagination.linkToPage,
totalCount: data?.data.total_count,
isTotalCountClipped: data?.data.is_total_count_clipped,
rowsPerPage: limit,
}}
/>
)}

{tableView === TableLayout.Vertical && (
<VerticalList>{/* TODO: Add when details page is ready */}</VerticalList>
)}
</SubPageCard>
</PageLayout>
)
}
7 changes: 6 additions & 1 deletion src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ import { Layer } from './oasis-nexus/api'
import { SearchScope } from './types/searchScope'
import { ProposalDetailsPage } from './app/pages/ProposalDetailsPage'
import { ConsensusBlocksPage } from './app/pages/ConsensusBlocksPage'
import { ConsensusAccountsPage } from 'app/pages/ConsensusAccountsPage'
import { ConsensusAccountsPage } from './app/pages/ConsensusAccountsPage'
import { ConsensusTransactionsPage } from './app/pages/ConsensusTransactionsPage'

const NetworkSpecificPart = () => (
<ThemeByNetwork network={useRequiredScopeParam().network}>
Expand Down Expand Up @@ -105,6 +106,10 @@ export const routes: RouteObject[] = [
path: `block`,
element: <ConsensusBlocksPage />,
},
{
path: 'tx',
element: <ConsensusTransactionsPage />,
},
],
},
{
Expand Down

0 comments on commit db64a37

Please sign in to comment.