From 4546aa2dd044a7cdc5d753ea852cbe8183e4314d Mon Sep 17 00:00:00 2001 From: Thomas Jeatt Date: Wed, 14 Sep 2022 09:46:15 +0100 Subject: [PATCH] feature: revert to previous versions of vault transaction tables --- src/pages/Vaults/Vault/ReplaceTable/index.tsx | 222 +++++++++++ src/pages/Vaults/Vault/VaultDashboard.tsx | 24 +- .../Vault/VaultIssueRequestsTable/index.tsx | 341 ++++++++++++++++ .../Vault/VaultRedeemRequestsTable/index.tsx | 367 ++++++++++++++++++ 4 files changed, 952 insertions(+), 2 deletions(-) create mode 100644 src/pages/Vaults/Vault/ReplaceTable/index.tsx create mode 100644 src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx create mode 100644 src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx diff --git a/src/pages/Vaults/Vault/ReplaceTable/index.tsx b/src/pages/Vaults/Vault/ReplaceTable/index.tsx new file mode 100644 index 0000000000..aa88c7c2be --- /dev/null +++ b/src/pages/Vaults/Vault/ReplaceTable/index.tsx @@ -0,0 +1,222 @@ +import { + CollateralCurrencyExt, + CollateralIdLiteral, + InterbtcPrimitivesVaultId, + ReplaceRequestExt, + stripHexPrefix, + WrappedCurrency +} from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { H256 } from '@polkadot/types/interfaces'; +import clsx from 'clsx'; +import * as React from 'react'; +import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; +import { useSelector } from 'react-redux'; +import { useTable } from 'react-table'; + +import { StoreType } from '@/common/types/util.types'; +import { displayMonetaryAmount, shortAddress } from '@/common/utils/utils'; +import ErrorFallback from '@/components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import InterlayTable, { + InterlayTableContainer, + InterlayTbody, + InterlayTd, + InterlayTh, + InterlayThead, + InterlayTr +} from '@/components/UI/InterlayTable'; +import { ACCOUNT_ID_TYPE_NAME } from '@/config/general'; +import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import SectionTitle from '@/parts/SectionTitle'; +import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; + +interface Props { + vaultAddress: string; + collateralTokenIdLiteral: CollateralIdLiteral; +} + +const ReplaceTable = ({ vaultAddress, collateralTokenIdLiteral }: Props): JSX.Element => { + const { t } = useTranslation(); + const { bridgeLoaded } = useSelector((state: StoreType) => state.general); + + const vaultId = window.bridge?.api.createType(ACCOUNT_ID_TYPE_NAME, vaultAddress); + const { + isIdle: replaceRequestsIdle, + isLoading: replaceRequestsLoading, + data: replaceRequests, + error: replaceRequestsError + } = useQuery, Error>( + [GENERIC_FETCHER, 'replace', 'mapReplaceRequests', vaultId], + genericFetcher>(), + { + enabled: !!bridgeLoaded && !!collateralTokenIdLiteral, + refetchInterval: 10000 + } + ); + useErrorHandler(replaceRequestsError); + + const columns = React.useMemo( + () => [ + { + Header: 'ID', + accessor: 'id', + classNames: ['text-center'], + Cell: function FormattedCell({ value }: { value: H256 }) { + return <>{stripHexPrefix(value.toString())}; + } + }, + { + Header: t('vault.creation_block'), + accessor: 'btcHeight', + classNames: ['text-center'] + }, + { + Header: t('vault.old_vault'), + accessor: 'oldVault', + classNames: ['text-center'], + Cell: function FormattedCell({ value }: { value: InterbtcPrimitivesVaultId }) { + return <>{shortAddress(value.accountId.toString())}; + } + }, + { + Header: t('vault.new_vault'), + accessor: 'newVault', + classNames: ['text-center'], + Cell: function FormattedCell({ value }: { value: InterbtcPrimitivesVaultId }) { + return <>{shortAddress(value.accountId.toString())}; + } + }, + { + Header: t('btc_address'), + accessor: 'btcAddress', + classNames: ['text-center'], + Cell: function FormattedCell({ value }: { value: string }) { + return <>{shortAddress(value)}; + } + }, + { + Header: WRAPPED_TOKEN_SYMBOL, + accessor: 'amount', + classNames: ['text-right'], + Cell: function FormattedCell({ value }: { value: MonetaryAmount }) { + return <>{displayMonetaryAmount(value)}; + } + }, + { + Header: t('griefing_collateral'), + accessor: 'collateral', + classNames: ['text-right'], + Cell: function FormattedCell({ value }: { value: MonetaryAmount }) { + return <>{displayMonetaryAmount(value)}; + } + }, + { + Header: t('status'), + accessor: 'status', + classNames: ['text-center'], + Cell: function FormattedCell({ value }: { value: ReplaceRequestExt['status'] }) { + let label; + if (value.isPending) { + label = t('pending'); + } else if (value.isCompleted) { + label = t('completed'); + } else if (value.isCancelled) { + label = t('cancelled'); + } else { + label = t('loading_ellipsis'); + } + return <>{label}; + } + } + ], + [t] + ); + + const data = replaceRequests + ? [ + ...replaceRequests + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + .filter((request) => request?.collateral?.currency?.ticker === collateralTokenIdLiteral) + .entries() + ].map(([key, value]) => ({ + id: key, + ...value + })) + : []; + const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ + columns, + data + }); + + if (replaceRequestsIdle || replaceRequestsLoading) { + return ; + } + if (replaceRequests === undefined) { + throw new Error('Something went wrong!'); + } + + return ( + + {t('vault.replace_requests')} + + + {headerGroups.map((headerGroup: any) => ( + // eslint-disable-next-line react/jsx-key + + {headerGroup.headers.map((column: any) => ( + // eslint-disable-next-line react/jsx-key + + {column.render('Header')} + + ))} + + ))} + + + {rows.map((row: any) => { + prepareRow(row); + + return ( + // eslint-disable-next-line react/jsx-key + + {row.cells.map((cell: any) => { + return ( + // eslint-disable-next-line react/jsx-key + + {cell.render('Cell')} + + ); + })} + + ); + })} + + + + ); +}; + +export default withErrorBoundary(ReplaceTable, { + FallbackComponent: ErrorFallback, + onReset: () => { + window.location.reload(); + } +}); diff --git a/src/pages/Vaults/Vault/VaultDashboard.tsx b/src/pages/Vaults/Vault/VaultDashboard.tsx index a5f8d777bb..557afc2883 100644 --- a/src/pages/Vaults/Vault/VaultDashboard.tsx +++ b/src/pages/Vaults/Vault/VaultDashboard.tsx @@ -15,8 +15,11 @@ import { getCurrency } from '@/utils/helpers/currencies'; import { useGetVaultData } from '@/utils/hooks/api/vaults/use-get-vault-data'; import { useGetVaultTransactions } from '@/utils/hooks/api/vaults/use-get-vault-transactions'; -import { InsightListItem, InsightsList, PageTitle, TransactionHistory, VaultInfo } from './components'; +import { InsightListItem, InsightsList, PageTitle, VaultInfo } from './components'; +import ReplaceTable from './ReplaceTable'; import { StyledCollateralSection, StyledRewards, StyledVaultCollateral } from './VaultDashboard.styles'; +import VaultIssueRequestsTable from './VaultIssueRequestsTable'; +import VaultRedeemRequestsTable from './VaultRedeemRequestsTable'; const VaultDashboard = (): JSX.Element => { const { vaultClientLoaded, address } = useSelector((state: StoreType) => state.general); @@ -115,7 +118,24 @@ const VaultDashboard = (): JSX.Element => { hasWithdrawRewardsBtn={!isReadOnlyVault} /> - + {collateralToken && ( + + )} + {collateralToken && ( + + )} + {collateralToken && ( + + )} ); diff --git a/src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx b/src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx new file mode 100644 index 0000000000..d7e3826d31 --- /dev/null +++ b/src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx @@ -0,0 +1,341 @@ +import { CollateralIdLiteral, IssueStatus } from '@interlay/interbtc-api'; +import clsx from 'clsx'; +import * as React from 'react'; +import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; +import { useTable } from 'react-table'; + +import { displayMonetaryAmount, formatDateTimePrecise, shortAddress } from '@/common/utils/utils'; +import ErrorFallback from '@/components/ErrorFallback'; +import ExternalLink from '@/components/ExternalLink'; +import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import InterlayPagination from '@/components/UI/InterlayPagination'; +import InterlayTable, { + InterlayTableContainer, + InterlayTbody, + InterlayTd, + InterlayTh, + InterlayThead, + InterlayTr +} from '@/components/UI/InterlayTable'; +import StatusCell from '@/components/UI/InterlayTable/StatusCell'; +import { BTC_EXPLORER_ADDRESS_API } from '@/config/blockstream-explorer-links'; +import SectionTitle from '@/parts/SectionTitle'; +import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; +import issuesFetcher, { getIssueWithStatus, ISSUES_FETCHER } from '@/services/fetchers/issues-fetcher'; +import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-block-number'; +import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; +import useStableParachainConfirmations from '@/services/hooks/use-stable-parachain-confirmations'; +import issueCountQuery from '@/services/queries/issue-count-query'; +import { TABLE_PAGE_LIMIT } from '@/utils/constants/general'; +import { QUERY_PARAMETERS } from '@/utils/constants/links'; +import useQueryParams from '@/utils/hooks/use-query-params'; +import useUpdateQueryParameters from '@/utils/hooks/use-update-query-parameters'; + +interface Props { + vaultAddress: string; + collateralTokenIdLiteral: CollateralIdLiteral; +} + +const VaultIssueRequestsTable = ({ vaultAddress, collateralTokenIdLiteral }: Props): JSX.Element | null => { + const queryParams = useQueryParams(); + const selectedPage = Number(queryParams.get(QUERY_PARAMETERS.PAGE)) || 1; + const selectedPageIndex = selectedPage - 1; + const updateQueryParameters = useUpdateQueryParameters(); + const { t } = useTranslation(); + + const { + isIdle: stableBitcoinConfirmationsIdle, + isLoading: stableBitcoinConfirmationsLoading, + data: stableBitcoinConfirmations, + error: stableBitcoinConfirmationsError + } = useStableBitcoinConfirmations(); + useErrorHandler(stableBitcoinConfirmationsError); + + const { + isIdle: currentActiveBlockNumberIdle, + isLoading: currentActiveBlockNumberLoading, + data: currentActiveBlockNumber, + error: currentActiveBlockNumberError + } = useCurrentActiveBlockNumber(); + useErrorHandler(currentActiveBlockNumberError); + + const { + isIdle: stableParachainConfirmationsIdle, + isLoading: stableParachainConfirmationsLoading, + data: stableParachainConfirmations, + error: stableParachainConfirmationsError + } = useStableParachainConfirmations(); + useErrorHandler(stableParachainConfirmationsError); + + const { + isIdle: issueRequestsTotalCountIdle, + isLoading: issueRequestsTotalCountLoading, + data: issueRequestsTotalCount, + error: issueRequestsTotalCountError + // TODO: should type properly (`Relay`) + } = useQuery, Error>( + [ + GRAPHQL_FETCHER, + issueCountQuery(`vault: {accountId_eq: "${vaultAddress}", collateralToken: {token_eq: ${collateralTokenIdLiteral}}}`) // TODO: add condition for asset_eq when the page is refactored for accepting ForeignAsset currencies too (cf. e.g. issued graph in dashboard for example) + ], + graphqlFetcher>() + ); + useErrorHandler(issueRequestsTotalCountError); + + const { + isIdle: issueRequestsIdle, + isLoading: issueRequestsLoading, + data: issueRequests, + error: issueRequestsError + // TODO: should type properly (`Relay`) + } = useQuery( + [ + ISSUES_FETCHER, + selectedPageIndex * TABLE_PAGE_LIMIT, // offset + TABLE_PAGE_LIMIT, // limit + `vault: {accountId_eq: "${vaultAddress}", collateralToken: {token_eq: ${collateralTokenIdLiteral}}}` // `WHERE` condition // TODO: add asset_eq, see comment above + ], + issuesFetcher + ); + useErrorHandler(issueRequestsError); + + const columns = React.useMemo( + () => [ + { + Header: t('id'), + accessor: 'id', + classNames: ['text-center'] + }, + { + Header: t('date_created'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: issue } }: any) { + return <>{formatDateTimePrecise(new Date(issue.request.timestamp))}; + } + }, + { + Header: t('vault.creation_block'), + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: issue } }: any) { + return <>{issue.request.height.absolute}; + } + }, + { + Header: t('last_update'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: issue } }: any) { + let date; + if (issue.execution) { + date = issue.execution.timestamp; + } else if (issue.cancellation) { + date = issue.cancellation.timestamp; + } else { + date = issue.request.timestamp; + } + + return <>{formatDateTimePrecise(new Date(date))}; + } + }, + { + Header: t('last_update_block'), + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: issue } }: any) { + let height; + if (issue.execution) { + height = issue.execution.height.absolute; + } else if (issue.cancellation) { + height = issue.cancellation.height.absolute; + } else { + height = issue.request.height.absolute; + } + + return <>{height}; + } + }, + { + Header: t('user'), + accessor: 'userParachainAddress', + classNames: ['text-center'], + Cell: function FormattedCell({ value }: { value: string }) { + return <>{shortAddress(value)}; + } + }, + { + Header: t('issue_page.amount'), + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: issue } }: any) { + let wrappedTokenAmount; + if (issue.execution) { + wrappedTokenAmount = issue.execution.amountWrapped; + } else { + wrappedTokenAmount = issue.request.amountWrapped; + } + + return <>{displayMonetaryAmount(wrappedTokenAmount)}; + } + }, + { + Header: t('griefing_collateral'), + accessor: 'griefingCollateral', + classNames: ['text-right'], + Cell: function FormattedCell({ value }: { value: any }) { + return <>{displayMonetaryAmount(value)}; + } + }, + { + Header: t('issue_page.vault_btc_address'), + accessor: 'vaultBackingAddress', + classNames: ['text-left'], + Cell: function FormattedCell({ value }: { value: string }) { + return {shortAddress(value)}; + } + }, + { + Header: t('status'), + accessor: 'status', + classNames: ['text-left'], + Cell: function FormattedCell({ value }: { value: IssueStatus }) { + return ( + + ); + } + } + ], + [t] + ); + + const data = + issueRequests === undefined || + stableBitcoinConfirmations === undefined || + stableParachainConfirmations === undefined || + currentActiveBlockNumber === undefined + ? [] + : issueRequests.map( + // TODO: should type properly (`Relay`) + (issueRequest: any) => + getIssueWithStatus( + issueRequest, + stableBitcoinConfirmations, + stableParachainConfirmations, + currentActiveBlockNumber + ) + ); + + const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ + columns, + data + }); + + if ( + stableBitcoinConfirmationsIdle || + stableBitcoinConfirmationsLoading || + stableParachainConfirmationsIdle || + stableParachainConfirmationsLoading || + currentActiveBlockNumberIdle || + currentActiveBlockNumberLoading || + issueRequestsTotalCountIdle || + issueRequestsTotalCountLoading || + issueRequestsIdle || + issueRequestsLoading + ) { + return ; + } + if (issueRequestsTotalCount === undefined) { + throw new Error('Something went wrong!'); + } + + const handlePageChange = ({ selected: newSelectedPageIndex }: { selected: number }) => { + updateQueryParameters({ + [QUERY_PARAMETERS.PAGE]: (newSelectedPageIndex + 1).toString() + }); + }; + + const totalSuccessfulIssueCount = issueRequestsTotalCount.data.issuesConnection.totalCount || 0; + const pageCount = Math.ceil(totalSuccessfulIssueCount / TABLE_PAGE_LIMIT); + + return ( + + {t('issue_requests')} + + + {headerGroups.map((headerGroup: any) => ( + // eslint-disable-next-line react/jsx-key + + {headerGroup.headers.map((column: any) => ( + // eslint-disable-next-line react/jsx-key + + {column.render('Header')} + + ))} + + ))} + + + {rows.map((row: any) => { + prepareRow(row); + + return ( + // eslint-disable-next-line react/jsx-key + + {row.cells.map((cell: any) => { + return ( + // eslint-disable-next-line react/jsx-key + + {cell.render('Cell')} + + ); + })} + + ); + })} + + + {pageCount > 0 && ( +
+ +
+ )} +
+ ); +}; + +export default withErrorBoundary(VaultIssueRequestsTable, { + FallbackComponent: ErrorFallback, + onReset: () => { + window.location.reload(); + } +}); diff --git a/src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx b/src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx new file mode 100644 index 0000000000..0b0b76440a --- /dev/null +++ b/src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx @@ -0,0 +1,367 @@ +import { CollateralIdLiteral, RedeemStatus } from '@interlay/interbtc-api'; +import clsx from 'clsx'; +import * as React from 'react'; +import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; +import { useTable } from 'react-table'; + +import { displayMonetaryAmount, formatDateTimePrecise, shortAddress, shortTxId } from '@/common/utils/utils'; +import ErrorFallback from '@/components/ErrorFallback'; +import ExternalLink from '@/components/ExternalLink'; +import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import InterlayPagination from '@/components/UI/InterlayPagination'; +import InterlayTable, { + InterlayTableContainer, + InterlayTbody, + InterlayTd, + InterlayTh, + InterlayThead, + InterlayTr +} from '@/components/UI/InterlayTable'; +import StatusCell from '@/components/UI/InterlayTable/StatusCell'; +import { BTC_EXPLORER_ADDRESS_API, BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +import SectionTitle from '@/parts/SectionTitle'; +import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; +import redeemsFetcher, { getRedeemWithStatus, REDEEMS_FETCHER } from '@/services/fetchers/redeems-fetcher'; +import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-block-number'; +import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; +import useStableParachainConfirmations from '@/services/hooks/use-stable-parachain-confirmations'; +import redeemCountQuery from '@/services/queries/redeem-count-query'; +import { TABLE_PAGE_LIMIT } from '@/utils/constants/general'; +import { QUERY_PARAMETERS } from '@/utils/constants/links'; +import useQueryParams from '@/utils/hooks/use-query-params'; +import useUpdateQueryParameters from '@/utils/hooks/use-update-query-parameters'; + +interface Props { + vaultAddress: string; + collateralTokenIdLiteral: CollateralIdLiteral; +} + +const VaultRedeemRequestsTable = ({ vaultAddress, collateralTokenIdLiteral }: Props): JSX.Element | null => { + const queryParams = useQueryParams(); + const selectedPage = Number(queryParams.get(QUERY_PARAMETERS.PAGE)) || 1; + const selectedPageIndex = selectedPage - 1; + const updateQueryParameters = useUpdateQueryParameters(); + const { t } = useTranslation(); + + const { + isIdle: stableBitcoinConfirmationsIdle, + isLoading: stableBitcoinConfirmationsLoading, + data: stableBitcoinConfirmations, + error: stableBitcoinConfirmationsError + } = useStableBitcoinConfirmations(); + useErrorHandler(stableBitcoinConfirmationsError); + + const { + isIdle: currentActiveBlockNumberIdle, + isLoading: currentActiveBlockNumberLoading, + data: currentActiveBlockNumber, + error: currentActiveBlockNumberError + } = useCurrentActiveBlockNumber(); + useErrorHandler(currentActiveBlockNumberError); + + const { + isIdle: stableParachainConfirmationsIdle, + isLoading: stableParachainConfirmationsLoading, + data: stableParachainConfirmations, + error: stableParachainConfirmationsError + } = useStableParachainConfirmations(); + useErrorHandler(stableParachainConfirmationsError); + + const { + isIdle: redeemRequestsTotalCountIdle, + isLoading: redeemRequestsTotalCountLoading, + data: redeemRequestsTotalCount, + error: redeemRequestsTotalCountError + // TODO: should type properly (`Relay`) + } = useQuery, Error>( + [ + GRAPHQL_FETCHER, + redeemCountQuery(`vault: {accountId_eq: "${vaultAddress}", collateralToken: {token_eq: ${collateralTokenIdLiteral}}}`) // TODO: add condition for asset_eq when the page is refactored for accepting ForeignAsset currencies too (cf. e.g. issued graph in dashboard for example) + ], + graphqlFetcher>() + ); + useErrorHandler(redeemRequestsTotalCountError); + + const { + isIdle: redeemRequestsIdle, + isLoading: redeemRequestsLoading, + data: redeemRequests, + error: redeemRequestsError + // TODO: should type properly (`Relay`) + } = useQuery( + [ + REDEEMS_FETCHER, + selectedPageIndex * TABLE_PAGE_LIMIT, // offset + TABLE_PAGE_LIMIT, // limit + `vault: {accountId_eq: "${vaultAddress}", collateralToken: {token_eq: ${collateralTokenIdLiteral}}}` // `WHERE` condition // TODO: add asset_eq, see comment above + ], + redeemsFetcher + ); + useErrorHandler(redeemRequestsError); + + const columns = React.useMemo( + () => [ + { + Header: t('id'), + accessor: 'id', + classNames: ['text-center'] + }, + { + Header: t('date_created'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: redeem } }: any) { + return <>{formatDateTimePrecise(new Date(redeem.request.timestamp))}; + } + }, + { + Header: t('vault.creation_block'), + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: redeem } }: any) { + return <>{redeem.request.height.absolute}; + } + }, + { + Header: t('last_update'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: redeem } }: any) { + let date; + if (redeem.execution) { + date = redeem.execution.timestamp; + } else if (redeem.cancellation) { + date = redeem.cancellation.timestamp; + } else { + date = redeem.request.timestamp; + } + + return <>{formatDateTimePrecise(new Date(date))}; + } + }, + { + Header: t('last_update_block'), + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: issue } }: any) { + let height; + if (issue.execution) { + height = issue.execution.height.absolute; + } else if (issue.cancellation) { + height = issue.cancellation.height.absolute; + } else { + height = issue.request.height.absolute; + } + + return <>{height}; + } + }, + { + Header: t('user'), + accessor: 'userParachainAddress', + classNames: ['text-center'], + Cell: function FormattedCell({ value }: { value: string }) { + return <>{shortAddress(value)}; + } + }, + { + Header: t('issue_page.amount'), + accessor: 'amountBTC', + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: redeem } }: any) { + return <>{displayMonetaryAmount(redeem.request.requestedAmountBacking)}; + } + }, + { + Header: t('redeem_page.btc_destination_address'), + accessor: 'userBackingAddress', + classNames: ['text-left'], + Cell: function FormattedCell({ value }: { value: string }) { + return {shortAddress(value)}; + } + }, + { + Header: t('issue_page.btc_transaction'), + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: redeemRequest } }: any) { + return ( + <> + {redeemRequest.status === RedeemStatus.Expired || + redeemRequest.status === RedeemStatus.Retried || + redeemRequest.status === RedeemStatus.Reimbursed ? ( + t('redeem_page.failed') + ) : ( + <> + {redeemRequest.backingPayment.btcTxId ? ( + { + event.stopPropagation(); + }} + > + {shortTxId(redeemRequest.backingPayment.btcTxId)} + + ) : ( + `${t('pending')}...` + )} + + )} + + ); + } + }, + { + Header: t('issue_page.confirmations'), + classNames: ['text-right'], + // TODO: should type properly (`Relay`) + Cell: function FormattedCell({ row: { original: redeem } }: any) { + const value = redeem.backingPayment.confirmations; + return <>{value === undefined ? t('not_applicable') : Math.max(value, 0)}; + } + }, + { + Header: t('status'), + accessor: 'status', + classNames: ['text-left'], + Cell: function FormattedCell({ value }: { value: RedeemStatus }) { + return ( + + ); + } + } + ], + [t] + ); + + const data = + redeemRequests === undefined || + stableBitcoinConfirmations === undefined || + stableParachainConfirmations === undefined || + currentActiveBlockNumber === undefined + ? [] + : redeemRequests.map( + // TODO: should type properly (`Relay`) + (redeemRequest: any) => + getRedeemWithStatus( + redeemRequest, + stableBitcoinConfirmations, + stableParachainConfirmations, + currentActiveBlockNumber + ) + ); + + const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ + columns, + data + }); + + if ( + stableBitcoinConfirmationsIdle || + stableBitcoinConfirmationsLoading || + stableParachainConfirmationsIdle || + stableParachainConfirmationsLoading || + currentActiveBlockNumberIdle || + currentActiveBlockNumberLoading || + redeemRequestsTotalCountIdle || + redeemRequestsTotalCountLoading || + redeemRequestsIdle || + redeemRequestsLoading + ) { + return ; + } + if (redeemRequestsTotalCount === undefined) { + throw new Error('Something went wrong!'); + } + + const handlePageChange = ({ selected: newSelectedPageIndex }: { selected: number }) => { + updateQueryParameters({ + [QUERY_PARAMETERS.PAGE]: (newSelectedPageIndex + 1).toString() + }); + }; + + const totalSuccessfulRedeemCount = redeemRequestsTotalCount.data.redeemsConnection.totalCount || 0; + const pageCount = Math.ceil(totalSuccessfulRedeemCount / TABLE_PAGE_LIMIT); + + return ( + + {t('redeem_requests')} + + + {headerGroups.map((headerGroup: any) => ( + // eslint-disable-next-line react/jsx-key + + {headerGroup.headers.map((column: any) => ( + // eslint-disable-next-line react/jsx-key + + {column.render('Header')} + + ))} + + ))} + + + {rows.map((row: any) => { + prepareRow(row); + + return ( + // eslint-disable-next-line react/jsx-key + + {row.cells.map((cell: any) => { + return ( + // eslint-disable-next-line react/jsx-key + + {cell.render('Cell')} + + ); + })} + + ); + })} + + + {pageCount > 0 && ( +
+ +
+ )} +
+ ); +}; + +export default withErrorBoundary(VaultRedeemRequestsTable, { + FallbackComponent: ErrorFallback, + onReset: () => { + window.location.reload(); + } +});