diff --git a/explorer/src/components/Consensus/Account/Account.tsx b/explorer/src/components/Consensus/Account/Account.tsx index 41bb1c91..87870b07 100644 --- a/explorer/src/components/Consensus/Account/Account.tsx +++ b/explorer/src/components/Consensus/Account/Account.tsx @@ -21,6 +21,7 @@ import { AccountExtrinsicList } from './AccountExtrinsicList' import { AccountGraphs } from './AccountGraphs' import { AccountRewardsHistory } from './AccountRewardsHistory' import { AccountTransfersList } from './AccountTransfersList' +import { BalanceHistory } from './BalanceHistory' import { QUERY_ACCOUNT_BY_ID } from './query' export const Account: FC = () => { @@ -86,6 +87,9 @@ export const Account: FC = () => { + + + ) : ( diff --git a/explorer/src/components/Consensus/Account/BalanceHistory.tsx b/explorer/src/components/Consensus/Account/BalanceHistory.tsx new file mode 100644 index 00000000..6b868803 --- /dev/null +++ b/explorer/src/components/Consensus/Account/BalanceHistory.tsx @@ -0,0 +1,203 @@ +/* eslint-disable camelcase */ +import { useApolloClient } from '@apollo/client' +import { SortingState } from '@tanstack/react-table' +import { SortedTable } from 'components/common/SortedTable' +import { Spinner } from 'components/common/Spinner' +import { Tooltip } from 'components/common/Tooltip' +import { NotFound } from 'components/layout/NotFound' +import { PAGE_SIZE } from 'constants/general' +import { INTERNAL_ROUTES, Routes } from 'constants/routes' +import { formatUnits } from 'ethers' +import { + BalanceHistoryByAccountIdQuery, + BalanceHistoryByAccountIdQueryVariables, + Order_By as OrderBy, + Consensus_Transfers_Select_Column as TransferColumn, + Consensus_Transfers_Bool_Exp as TransferWhere, +} from 'gql/graphql' +import useChains from 'hooks/useChains' +import { useSquidQuery } from 'hooks/useSquidQuery' +import { useWindowFocus } from 'hooks/useWindowFocus' +import Link from 'next/link' +import { FC, useCallback, useEffect, useMemo, useState } from 'react' +import { useInView } from 'react-intersection-observer' +import type { Cell } from 'types/table' +import { downloadFullData } from 'utils/downloadFullData' +import { bigNumberToNumber } from 'utils/number' +import { countTablePages } from 'utils/table' +import { QUERY_ACCOUNT_BALANCE_HISTORY } from './query' + +type Props = { + accountId: string +} + +type Row = BalanceHistoryByAccountIdQuery['consensus_account_histories'][0] + +export const BalanceHistory: FC = ({ accountId }) => { + const { ref, inView } = useInView() + const [sorting, setSorting] = useState([ + { id: TransferColumn.CreatedAt, desc: true }, + ]) + const [pagination, setPagination] = useState({ + pageSize: PAGE_SIZE, + pageIndex: 0, + }) + const { network, tokenSymbol } = useChains() + const apolloClient = useApolloClient() + const inFocus = useWindowFocus() + + const orderBy = useMemo( + () => + sorting && sorting.length > 0 + ? sorting[0].id.endsWith('aggregate') + ? { [sorting[0].id]: sorting[0].desc ? { count: OrderBy.Desc } : { count: OrderBy.Asc } } + : { [sorting[0].id]: sorting[0].desc ? OrderBy.Desc : OrderBy.Asc } + : { id: OrderBy.Asc }, + [sorting], + ) + + const where: TransferWhere = useMemo(() => ({ id: { _eq: accountId } }), [accountId]) + + const variables = useMemo(() => { + return { + limit: pagination.pageSize, + offset: pagination.pageIndex > 0 ? pagination.pageIndex * pagination.pageSize : undefined, + orderBy, + where, + } + }, [orderBy, pagination.pageIndex, pagination.pageSize, where]) + + const { data, loading, setIsVisible } = useSquidQuery< + BalanceHistoryByAccountIdQuery, + BalanceHistoryByAccountIdQueryVariables + >(QUERY_ACCOUNT_BALANCE_HISTORY, { + variables, + skip: !inFocus, + pollInterval: 6000, + }) + + const fullDataDownloader = useCallback( + () => + downloadFullData(apolloClient, QUERY_ACCOUNT_BALANCE_HISTORY, 'consensus_account_histories', { + orderBy, + where, + }), + [apolloClient, orderBy, where], + ) + + const histories = useMemo(() => data && data.consensus_account_histories, [data]) + const totalCount = useMemo( + () => + data && data.consensus_account_histories_aggregate + ? data.consensus_account_histories_aggregate.aggregate?.count + : 0, + [data], + ) + const pageCount = useMemo( + () => (totalCount ? countTablePages(totalCount, pagination.pageSize) : 0), + [totalCount, pagination.pageSize], + ) + + const columns = useMemo( + () => [ + { + accessorKey: 'created_at', + header: 'Block', + enableSorting: true, + cell: ({ row }: Cell) => ( +
+ +
{row.original.created_at}
+ +
+ ), + }, + { + accessorKey: 'nonce', + header: 'Nonce', + enableSorting: true, + cell: ({ row }: Cell) => ( +
{row.original.nonce}
+ ), + }, + { + accessorKey: 'free', + header: 'Free', + enableSorting: true, + cell: ({ row }: Cell) => ( +
+ + {`${bigNumberToNumber(row.original.free, 6)} ${tokenSymbol}`} + +
+ ), + }, + { + accessorKey: 'reserved', + header: 'Reserved', + enableSorting: true, + cell: ({ row }: Cell) => ( +
+ + {`${bigNumberToNumber(row.original.reserved, 6)} ${tokenSymbol}`} + +
+ ), + }, + { + accessorKey: 'total', + header: 'Total', + enableSorting: true, + cell: ({ row }: Cell) => ( +
+ + {`${bigNumberToNumber(row.original.total, 6)} ${tokenSymbol}`} + +
+ ), + }, + ], + [network, tokenSymbol], + ) + + const noData = useMemo(() => { + if (loading) return + if (!data) return + return null + }, [data, loading]) + + useEffect(() => { + setIsVisible(inView) + }, [inView, setIsVisible]) + + return ( +
+
+ {!loading && histories ? ( + + ) : ( + noData + )} +
+
+ ) +} diff --git a/explorer/src/components/Consensus/Account/query.ts b/explorer/src/components/Consensus/Account/query.ts index baa51eb5..a6530d12 100644 --- a/explorer/src/components/Consensus/Account/query.ts +++ b/explorer/src/components/Consensus/Account/query.ts @@ -179,6 +179,31 @@ export const QUERY_ACCOUNT_TRANSFERS = gql` } ` +export const QUERY_ACCOUNT_BALANCE_HISTORY = gql` + query BalanceHistoryByAccountId( + $limit: Int! + $offset: Int + $where: consensus_account_histories_bool_exp + $orderBy: [consensus_account_histories_order_by!]! + ) { + consensus_account_histories_aggregate(where: $where) { + aggregate { + count + } + } + consensus_account_histories(order_by: $orderBy, limit: $limit, offset: $offset, where: $where) { + id: uuid + reserved + total + nonce + free + created_at + updated_at + _block_range + } + } +` + export const QUERY_ALL_REWARDS_FOR_ACCOUNT_BY_ID = gql` query AllRewardForAccountById($accountId: String!) { consensus_rewards(where: { account_id: { _eq: $accountId }, amount: { _gt: 0 } }, limit: 1) {