From fa0ba920a8e119d00205d9c0a32e986984dbea44 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 1 Mar 2024 10:23:29 +0100 Subject: [PATCH 01/12] Prepare API layer for validator debonding card --- src/oasis-nexus/api.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 8ce87bb40..2c671ae2d 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -98,6 +98,12 @@ declare module './generated/api' { layer: typeof Layer.consensus ticker: Ticker } + + export interface DebondingDelegation { + layer: typeof Layer.consensus + network: Network + ticker: Ticker + } } export const isAccountEmpty = (account: RuntimeAccount) => { @@ -985,3 +991,33 @@ export const useGetConsensusAccountsAddressDelegations: typeof generated.useGetC }, }) } + +export const useGetConsensusAccountsAddressDebondingDelegationsTo: typeof generated.useGetConsensusAccountsAddressDebondingDelegationsTo = + (network, address, params?, options?) => { + const ticker = getTokensForScope({ network, layer: Layer.consensus })[0].ticker + return generated.useGetConsensusAccountsAddressDebondingDelegationsTo(network, address, params, { + ...options, + request: { + ...options?.request, + transformResponse: [ + ...arrayify(axios.defaults.transformResponse), + (data: generated.DebondingDelegationList, headers, status) => { + if (status !== 200) return data + return { + ...data, + debonding_delegations: data.debonding_delegations.map(delegation => { + return { + ...delegation, + amount: fromBaseUnits(delegation.amount, consensusDecimals), + layer: Layer.consensus, + network, + ticker, + } + }), + } + }, + ...arrayify(options?.request?.transformResponse), + ], + }, + }) + } From 253b41c3cfd22b992df19b4a4b9a61938ae5034f Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Mon, 11 Mar 2024 10:47:23 +0100 Subject: [PATCH 02/12] Create debonding delegation list component --- .../Delegations/DebondingDelegations.tsx | 68 +++++++++++++++++++ .../components/Delegations/Delegations.tsx | 56 +++++++++++++++ src/app/components/Delegations/index.tsx | 58 +--------------- src/locales/en/translation.json | 3 + 4 files changed, 129 insertions(+), 56 deletions(-) create mode 100644 src/app/components/Delegations/DebondingDelegations.tsx create mode 100644 src/app/components/Delegations/Delegations.tsx diff --git a/src/app/components/Delegations/DebondingDelegations.tsx b/src/app/components/Delegations/DebondingDelegations.tsx new file mode 100644 index 000000000..489947ff9 --- /dev/null +++ b/src/app/components/Delegations/DebondingDelegations.tsx @@ -0,0 +1,68 @@ +import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { DebondingDelegation } from '../../../oasis-nexus/api' +import { Table, TableCellAlign, TableColProps } from '../Table' +import { TablePaginationProps } from '../Table/TablePagination' +import { RoundedBalance } from '../RoundedBalance' +import { AccountLink } from '../Account/AccountLink' + +type DebondingDelegationsProps = { + debondingDelegations?: DebondingDelegation[] + isLoading: boolean + limit: number + pagination: false | TablePaginationProps +} + +export const DebondingDelegations: FC = ({ + debondingDelegations, + isLoading, + limit, + pagination, +}) => { + const { t } = useTranslation() + + const tableColumns: TableColProps[] = [ + { key: 'delegator', content: t('common.address') }, + { key: 'amount', content: t('validator.amount'), align: TableCellAlign.Right }, + { key: 'shares', content: t('validator.shares'), align: TableCellAlign.Right }, + { key: 'debondingEnd', content: t('validator.debondingEnd'), align: TableCellAlign.Right }, + ] + const tableRows = debondingDelegations?.map(delegation => ({ + key: delegation.delegator, + data: [ + { + content: , + key: 'delegator', + }, + { + align: TableCellAlign.Right, + content: , + key: 'amount', + }, + + { + align: TableCellAlign.Right, + // TODO: Add when API returns correct value + content: <>-, + key: 'shares', + }, + { + align: TableCellAlign.Right, + // TODO: Add when API returns correct value and provides current epoch + content: <>-, + key: 'debondingEnd', + }, + ], + })) + + return ( + + ) +} diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx new file mode 100644 index 000000000..b1f39a20c --- /dev/null +++ b/src/app/components/Delegations/Delegations.tsx @@ -0,0 +1,56 @@ +import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { Table, TableCellAlign, TableColProps } from '../../components/Table' +import { Delegation } from '../../../oasis-nexus/api' +import { TablePaginationProps } from '../Table/TablePagination' +import { RoundedBalance } from '../RoundedBalance' +import { ValidatorLink } from '../Validators/ValidatorLink' + +type DelegationsProps = { + delegations?: Delegation[] + isLoading: boolean + limit: number + pagination: false | TablePaginationProps +} + +export const Delegations: FC = ({ delegations, isLoading, limit, pagination }) => { + const { t } = useTranslation() + + const tableColumns: TableColProps[] = [ + { key: 'name', content: t('validator.title') }, + { align: TableCellAlign.Right, key: 'shares', content: t('validator.shares') }, + { align: TableCellAlign.Right, key: 'staked', content: t('common.staked') }, + ] + const tableRows = delegations?.map(delegation => ({ + key: delegation.validator, + data: [ + { + // TODO: Use trimmed name when API is updated + content: , + key: 'name', + }, + { + align: TableCellAlign.Right, + content: , + key: 'shares', + }, + { + align: TableCellAlign.Right, + + content: , + key: 'staked', + }, + ], + })) + + return ( +
+ ) +} diff --git a/src/app/components/Delegations/index.tsx b/src/app/components/Delegations/index.tsx index b1f39a20c..7d180df20 100644 --- a/src/app/components/Delegations/index.tsx +++ b/src/app/components/Delegations/index.tsx @@ -1,56 +1,2 @@ -import { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { Table, TableCellAlign, TableColProps } from '../../components/Table' -import { Delegation } from '../../../oasis-nexus/api' -import { TablePaginationProps } from '../Table/TablePagination' -import { RoundedBalance } from '../RoundedBalance' -import { ValidatorLink } from '../Validators/ValidatorLink' - -type DelegationsProps = { - delegations?: Delegation[] - isLoading: boolean - limit: number - pagination: false | TablePaginationProps -} - -export const Delegations: FC = ({ delegations, isLoading, limit, pagination }) => { - const { t } = useTranslation() - - const tableColumns: TableColProps[] = [ - { key: 'name', content: t('validator.title') }, - { align: TableCellAlign.Right, key: 'shares', content: t('validator.shares') }, - { align: TableCellAlign.Right, key: 'staked', content: t('common.staked') }, - ] - const tableRows = delegations?.map(delegation => ({ - key: delegation.validator, - data: [ - { - // TODO: Use trimmed name when API is updated - content: , - key: 'name', - }, - { - align: TableCellAlign.Right, - content: , - key: 'shares', - }, - { - align: TableCellAlign.Right, - - content: , - key: 'staked', - }, - ], - })) - - return ( -
- ) -} +export * from './Delegations' +export * from './DebondingDelegations' diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index cef6be6c4..65e47de55 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -600,6 +600,7 @@ }, "validator": { "active": "Active", + "amount": "Amount", "balanceDistribution": "Balance Distribution", "blockWithHeight": "Block {{height}}", "boundsNotSet": "No bounds set (0% - 100%)", @@ -608,6 +609,7 @@ "commissionBounds": "Commission Bounds", "commissionRates": "Commission Rates", "cumulativeVoting": "Cumulative Voting %", + "debondingEnd": "End of debonding", "delegators": "Delegators", "entityId": "Entity ID", "externalLink": "External Link", @@ -630,6 +632,7 @@ "startDate": "Start Date", "title": "Validator", "totalShare": "Total Share", + "undelegations": "Undelegations", "uptime": "Uptime", "voting": "Voting %", "votingPower": "Voting Power" From a68255b35c8bd3243187a0406f17ea1a00279d61 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 1 Mar 2024 10:25:58 +0100 Subject: [PATCH 03/12] Enable debonding to card in validator details page --- .changelog/1310.trivial.md | 1 + .../DebondingDelegationsCard.tsx | 65 +++++++++++++++++++ src/app/pages/ValidatorDetailsPage/index.tsx | 10 ++- src/locales/en/translation.json | 1 + src/routes.tsx | 5 ++ 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 .changelog/1310.trivial.md create mode 100644 src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx diff --git a/.changelog/1310.trivial.md b/.changelog/1310.trivial.md new file mode 100644 index 000000000..5dce57230 --- /dev/null +++ b/.changelog/1310.trivial.md @@ -0,0 +1 @@ +Add validator delegators and debonding delegations cards diff --git a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx new file mode 100644 index 000000000..a0ed46c1c --- /dev/null +++ b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx @@ -0,0 +1,65 @@ +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 { useGetConsensusAccountsAddressDebondingDelegationsTo } from '../../../oasis-nexus/api' +import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE as limit } from '../../config' +import { ErrorBoundary } from '../../components/ErrorBoundary' +import { LinkableDiv } from '../../components/PageLayout/LinkableDiv' +import { CardEmptyState } from '../../components/CardEmptyState' +import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' +import { DebondingDelegations } from '../../components/Delegations' +import { ValidatorDetailsContext } from './hooks' + +export const validatorDebondingContainerId = 'debonding' + +export const DebondingDelegationsCard: FC = ({ scope, address }) => { + const { t } = useTranslation() + + return ( + + + + + + + + + + + ) +} + +const DebondingDelegationsView: FC = ({ scope, address }) => { + const { t } = useTranslation() + const { network } = scope + const pagination = useSearchParamsPagination('page') + const offset = (pagination.selectedPage - 1) * limit + const debondingQuery = useGetConsensusAccountsAddressDebondingDelegationsTo(network, address, { + limit, + offset, + }) + const { isFetched, isLoading, data } = debondingQuery + const debondingDelegations = data?.data.debonding_delegations + + return ( + <> + {isFetched && !debondingDelegations?.length && ( + + )} + + + ) +} diff --git a/src/app/pages/ValidatorDetailsPage/index.tsx b/src/app/pages/ValidatorDetailsPage/index.tsx index 74fafc4aa..25cedd7de 100644 --- a/src/app/pages/ValidatorDetailsPage/index.tsx +++ b/src/app/pages/ValidatorDetailsPage/index.tsx @@ -24,6 +24,7 @@ import { SignedBlocks } from './SignedBlocks' import { StakingTrend } from './StakingTrend' import { ProposedBlocks } from './ProposedBlocks' import { ValidatorDetailsContext } from './hooks' +import { validatorDebondingContainerId } from './DebondingDelegationsCard' export const StyledGrid = styled(Grid)(({ theme }) => ({ [theme.breakpoints.up('sm')]: { @@ -41,6 +42,7 @@ export const ValidatorDetailsPage: FC = () => { const { isLoading, data } = validatorQuery const validator = data?.data const transactionsLink = useHref('') + const debondingDelegationsLink = useHref(`debonding-delegations#${validatorDebondingContainerId}`) const context: ValidatorDetailsContext = { scope, address } return ( @@ -58,7 +60,13 @@ export const ValidatorDetailsPage: FC = () => { - + ) } diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 65e47de55..a1cfc65ff 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -611,6 +611,7 @@ "cumulativeVoting": "Cumulative Voting %", "debondingEnd": "End of debonding", "delegators": "Delegators", + "emptyDebondingList": "No one is debonding delegations from this validator.", "entityId": "Entity ID", "externalLink": "External Link", "externalLinkDescriotion": "Oasis is not responsible for any content on
external pages.", diff --git a/src/routes.tsx b/src/routes.tsx index bb82876cd..f409b2247 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -44,6 +44,7 @@ import { ValidatorsPage } from './app/pages/ValidatorsPage' import { ProposalsPage } from './app/pages/ProposalsPage' import { ValidatorDetailsPage } from './app/pages/ValidatorDetailsPage' import { useValidatorDetailsProps } from './app/pages/ValidatorDetailsPage/hooks' +import { DebondingDelegationsCard } from './app/pages/ValidatorDetailsPage/DebondingDelegationsCard' import { Layer } from './oasis-nexus/api' import { SearchScope } from './types/searchScope' import { ProposalDetailsPage } from './app/pages/ProposalDetailsPage' @@ -155,6 +156,10 @@ export const routes: RouteObject[] = [ path: '', Component: () => , }, + { + path: 'debonding-delegations', + Component: () => , + }, ], }, { From ff1b79cae2dea9744965305e2402c4d5f3b651ca Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 1 Mar 2024 12:15:18 +0100 Subject: [PATCH 04/12] Rename delegations entry file --- .../Delegations/DebondingDelegations.tsx | 68 ------------------- .../components/Delegations/Delegations.tsx | 56 --------------- src/app/components/Delegations/index.tsx | 65 +++++++++++++++++- .../DebondingDelegationsCard.tsx | 4 +- 4 files changed, 65 insertions(+), 128 deletions(-) delete mode 100644 src/app/components/Delegations/DebondingDelegations.tsx delete mode 100644 src/app/components/Delegations/Delegations.tsx diff --git a/src/app/components/Delegations/DebondingDelegations.tsx b/src/app/components/Delegations/DebondingDelegations.tsx deleted file mode 100644 index 489947ff9..000000000 --- a/src/app/components/Delegations/DebondingDelegations.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { DebondingDelegation } from '../../../oasis-nexus/api' -import { Table, TableCellAlign, TableColProps } from '../Table' -import { TablePaginationProps } from '../Table/TablePagination' -import { RoundedBalance } from '../RoundedBalance' -import { AccountLink } from '../Account/AccountLink' - -type DebondingDelegationsProps = { - debondingDelegations?: DebondingDelegation[] - isLoading: boolean - limit: number - pagination: false | TablePaginationProps -} - -export const DebondingDelegations: FC = ({ - debondingDelegations, - isLoading, - limit, - pagination, -}) => { - const { t } = useTranslation() - - const tableColumns: TableColProps[] = [ - { key: 'delegator', content: t('common.address') }, - { key: 'amount', content: t('validator.amount'), align: TableCellAlign.Right }, - { key: 'shares', content: t('validator.shares'), align: TableCellAlign.Right }, - { key: 'debondingEnd', content: t('validator.debondingEnd'), align: TableCellAlign.Right }, - ] - const tableRows = debondingDelegations?.map(delegation => ({ - key: delegation.delegator, - data: [ - { - content: , - key: 'delegator', - }, - { - align: TableCellAlign.Right, - content: , - key: 'amount', - }, - - { - align: TableCellAlign.Right, - // TODO: Add when API returns correct value - content: <>-, - key: 'shares', - }, - { - align: TableCellAlign.Right, - // TODO: Add when API returns correct value and provides current epoch - content: <>-, - key: 'debondingEnd', - }, - ], - })) - - return ( -
- ) -} diff --git a/src/app/components/Delegations/Delegations.tsx b/src/app/components/Delegations/Delegations.tsx deleted file mode 100644 index b1f39a20c..000000000 --- a/src/app/components/Delegations/Delegations.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { FC } from 'react' -import { useTranslation } from 'react-i18next' -import { Table, TableCellAlign, TableColProps } from '../../components/Table' -import { Delegation } from '../../../oasis-nexus/api' -import { TablePaginationProps } from '../Table/TablePagination' -import { RoundedBalance } from '../RoundedBalance' -import { ValidatorLink } from '../Validators/ValidatorLink' - -type DelegationsProps = { - delegations?: Delegation[] - isLoading: boolean - limit: number - pagination: false | TablePaginationProps -} - -export const Delegations: FC = ({ delegations, isLoading, limit, pagination }) => { - const { t } = useTranslation() - - const tableColumns: TableColProps[] = [ - { key: 'name', content: t('validator.title') }, - { align: TableCellAlign.Right, key: 'shares', content: t('validator.shares') }, - { align: TableCellAlign.Right, key: 'staked', content: t('common.staked') }, - ] - const tableRows = delegations?.map(delegation => ({ - key: delegation.validator, - data: [ - { - // TODO: Use trimmed name when API is updated - content: , - key: 'name', - }, - { - align: TableCellAlign.Right, - content: , - key: 'shares', - }, - { - align: TableCellAlign.Right, - - content: , - key: 'staked', - }, - ], - })) - - return ( -
- ) -} diff --git a/src/app/components/Delegations/index.tsx b/src/app/components/Delegations/index.tsx index 7d180df20..9538b8834 100644 --- a/src/app/components/Delegations/index.tsx +++ b/src/app/components/Delegations/index.tsx @@ -1,2 +1,63 @@ -export * from './Delegations' -export * from './DebondingDelegations' +import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { DebondingDelegation } from '../../../oasis-nexus/api' +import { Table, TableCellAlign, TableColProps } from '../Table' +import { TablePaginationProps } from '../Table/TablePagination' +import { RoundedBalance } from '../RoundedBalance' +import { AccountLink } from '../Account/AccountLink' + +type DelegationsProps = { + debondingDelegations?: DebondingDelegation[] + isLoading: boolean + limit: number + pagination: false | TablePaginationProps +} + +export const Delegations: FC = ({ debondingDelegations, isLoading, limit, pagination }) => { + const { t } = useTranslation() + + const tableColumns: TableColProps[] = [ + { key: 'delegator', content: t('common.address') }, + { key: 'amount', content: t('validator.amount'), align: TableCellAlign.Right }, + { key: 'shares', content: t('validator.shares'), align: TableCellAlign.Right }, + { key: 'debondingEnd', content: t('validator.debondingEnd'), align: TableCellAlign.Right }, + ] + const tableRows = debondingDelegations?.map(delegation => ({ + key: delegation.delegator, + data: [ + { + content: , + key: 'delegator', + }, + { + align: TableCellAlign.Right, + content: , + key: 'amount', + }, + + { + align: TableCellAlign.Right, + // TODO: Add when API returns correct value + content: <>-, + key: 'shares', + }, + { + align: TableCellAlign.Right, + // TODO: Add when API returns correct value and provides current epoch + content: <>-, + key: 'debondingEnd', + }, + ], + })) + + return ( +
+ ) +} diff --git a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx index a0ed46c1c..adfe18957 100644 --- a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx +++ b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx @@ -9,7 +9,7 @@ import { ErrorBoundary } from '../../components/ErrorBoundary' import { LinkableDiv } from '../../components/PageLayout/LinkableDiv' import { CardEmptyState } from '../../components/CardEmptyState' import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' -import { DebondingDelegations } from '../../components/Delegations' +import { Delegations } from '../../components/Delegations' import { ValidatorDetailsContext } from './hooks' export const validatorDebondingContainerId = 'debonding' @@ -48,7 +48,7 @@ const DebondingDelegationsView: FC = ({ scope, address {isFetched && !debondingDelegations?.length && ( )} - Date: Fri, 1 Mar 2024 12:17:36 +0100 Subject: [PATCH 05/12] Prepare API layer to handle validator delegations --- src/oasis-nexus/api.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 2c671ae2d..de03fe730 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -1021,3 +1021,33 @@ export const useGetConsensusAccountsAddressDebondingDelegationsTo: typeof genera }, }) } + +export const useGetConsensusAccountsAddressDelegationsTo: typeof generated.useGetConsensusAccountsAddressDelegationsTo = + (network, address, params?, options?) => { + const ticker = getTokensForScope({ network, layer: Layer.consensus })[0].ticker + return generated.useGetConsensusAccountsAddressDelegationsTo(network, address, params, { + ...options, + request: { + ...options?.request, + transformResponse: [ + ...arrayify(axios.defaults.transformResponse), + (data: generated.DelegationList, headers, status) => { + if (status !== 200) return data + return { + ...data, + delegations: data.delegations.map(delegation => { + return { + ...delegation, + amount: fromBaseUnits(delegation.amount, consensusDecimals), + layer: Layer.consensus, + network, + ticker, + } + }), + } + }, + ...arrayify(options?.request?.transformResponse), + ], + }, + }) + } From bde870e66bb04a804ae2a783df0912c312f6aa96 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 1 Mar 2024 12:25:01 +0100 Subject: [PATCH 06/12] Handle delegations and debonding delegations in the same component --- src/app/components/Delegations/index.tsx | 37 +++++++++++++------ .../DebondingDelegationsCard.tsx | 3 +- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/app/components/Delegations/index.tsx b/src/app/components/Delegations/index.tsx index 9538b8834..6d03604b0 100644 --- a/src/app/components/Delegations/index.tsx +++ b/src/app/components/Delegations/index.tsx @@ -1,28 +1,37 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { DebondingDelegation } from '../../../oasis-nexus/api' +import { DebondingDelegation, Delegation } from '../../../oasis-nexus/api' import { Table, TableCellAlign, TableColProps } from '../Table' import { TablePaginationProps } from '../Table/TablePagination' import { RoundedBalance } from '../RoundedBalance' import { AccountLink } from '../Account/AccountLink' type DelegationsProps = { - debondingDelegations?: DebondingDelegation[] + debonding?: boolean + delegations?: Delegation[] | DebondingDelegation[] isLoading: boolean limit: number pagination: false | TablePaginationProps } -export const Delegations: FC = ({ debondingDelegations, isLoading, limit, pagination }) => { +export const Delegations: FC = ({ + debonding, + delegations, + isLoading, + limit, + pagination, +}) => { const { t } = useTranslation() const tableColumns: TableColProps[] = [ { key: 'delegator', content: t('common.address') }, { key: 'amount', content: t('validator.amount'), align: TableCellAlign.Right }, { key: 'shares', content: t('validator.shares'), align: TableCellAlign.Right }, - { key: 'debondingEnd', content: t('validator.debondingEnd'), align: TableCellAlign.Right }, + ...(debonding + ? [{ key: 'debondingEnd', content: t('validator.debondingEnd'), align: TableCellAlign.Right }] + : []), ] - const tableRows = debondingDelegations?.map(delegation => ({ + const tableRows = delegations?.map(delegation => ({ key: delegation.delegator, data: [ { @@ -41,12 +50,16 @@ export const Delegations: FC = ({ debondingDelegations, isLoad content: <>-, key: 'shares', }, - { - align: TableCellAlign.Right, - // TODO: Add when API returns correct value and provides current epoch - content: <>-, - key: 'debondingEnd', - }, + ...(debonding + ? [ + { + align: TableCellAlign.Right, + // TODO: Add when API returns correct value and provides current epoch + content: <>-, + key: 'debondingEnd', + }, + ] + : []), ], })) @@ -55,7 +68,7 @@ export const Delegations: FC = ({ debondingDelegations, isLoad columns={tableColumns} rows={tableRows} rowsNumber={limit} - name={t('validator.undelegations')} + name={debonding ? t('validator.undelegations') : t('validator.delegators')} isLoading={isLoading} pagination={pagination} /> diff --git a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx index adfe18957..80fe2de96 100644 --- a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx +++ b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx @@ -49,7 +49,8 @@ const DebondingDelegationsView: FC = ({ scope, address )} Date: Fri, 1 Mar 2024 12:40:42 +0100 Subject: [PATCH 07/12] Enable delegators to card in validator details page --- .../ValidatorDetailsPage/DelegatorsCard.tsx | 63 +++++++++++++++++++ src/app/pages/ValidatorDetailsPage/index.tsx | 3 + src/locales/en/translation.json | 1 + src/routes.tsx | 5 ++ 4 files changed, 72 insertions(+) create mode 100644 src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx diff --git a/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx b/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx new file mode 100644 index 000000000..990f56809 --- /dev/null +++ b/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx @@ -0,0 +1,63 @@ +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 { useGetConsensusAccountsAddressDelegationsTo } from '../../../oasis-nexus/api' +import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE as limit } from '../../config' +import { ErrorBoundary } from '../../components/ErrorBoundary' +import { LinkableDiv } from '../../components/PageLayout/LinkableDiv' +import { CardEmptyState } from '../../components/CardEmptyState' +import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' +import { Delegations } from '../../components/Delegations' +import { ValidatorDetailsContext } from './hooks' + +export const validatorDelegatorsContainerId = 'delegators' + +export const DelegatorsCard: FC = ({ scope, address }) => { + const { t } = useTranslation() + + return ( + + + + + + + + + + + ) +} + +const DelegatorsCardView: FC = ({ scope, address }) => { + const { t } = useTranslation() + const { network } = scope + const pagination = useSearchParamsPagination('page') + const offset = (pagination.selectedPage - 1) * limit + const delegationsQuery = useGetConsensusAccountsAddressDelegationsTo(network, address, { + limit, + offset, + }) + const { isFetched, isLoading, data } = delegationsQuery + const delegations = data?.data.delegations + + return ( + <> + {isFetched && !delegations?.length && } + + + ) +} diff --git a/src/app/pages/ValidatorDetailsPage/index.tsx b/src/app/pages/ValidatorDetailsPage/index.tsx index 25cedd7de..608d0dcfd 100644 --- a/src/app/pages/ValidatorDetailsPage/index.tsx +++ b/src/app/pages/ValidatorDetailsPage/index.tsx @@ -25,6 +25,7 @@ import { StakingTrend } from './StakingTrend' import { ProposedBlocks } from './ProposedBlocks' import { ValidatorDetailsContext } from './hooks' import { validatorDebondingContainerId } from './DebondingDelegationsCard' +import { validatorDelegatorsContainerId } from './DelegatorsCard' export const StyledGrid = styled(Grid)(({ theme }) => ({ [theme.breakpoints.up('sm')]: { @@ -42,6 +43,7 @@ export const ValidatorDetailsPage: FC = () => { const { isLoading, data } = validatorQuery const validator = data?.data const transactionsLink = useHref('') + const delegatorsLink = useHref(`delegators#${validatorDelegatorsContainerId}`) const debondingDelegationsLink = useHref(`debonding-delegations#${validatorDebondingContainerId}`) const context: ValidatorDetailsContext = { scope, address } @@ -63,6 +65,7 @@ export const ValidatorDetailsPage: FC = () => { , }, + { + path: 'delegators', + Component: () => , + }, { path: 'debonding-delegations', Component: () => , From 95d3d887ee5e8d75efa918dd2c8a4a086b5c02b7 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 1 Mar 2024 13:02:20 +0100 Subject: [PATCH 08/12] Show shares value in delegations list --- src/app/components/Delegations/index.tsx | 6 ++---- src/oasis-nexus/api.ts | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/components/Delegations/index.tsx b/src/app/components/Delegations/index.tsx index 6d03604b0..75049d317 100644 --- a/src/app/components/Delegations/index.tsx +++ b/src/app/components/Delegations/index.tsx @@ -40,14 +40,12 @@ export const Delegations: FC = ({ }, { align: TableCellAlign.Right, - content: , + content: , key: 'amount', }, - { align: TableCellAlign.Right, - // TODO: Add when API returns correct value - content: <>-, + content: , key: 'shares', }, ...(debonding diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index de03fe730..beb8221dc 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -1009,6 +1009,7 @@ export const useGetConsensusAccountsAddressDebondingDelegationsTo: typeof genera return { ...delegation, amount: fromBaseUnits(delegation.amount, consensusDecimals), + shares: fromBaseUnits(delegation.shares, consensusDecimals), layer: Layer.consensus, network, ticker, @@ -1039,6 +1040,7 @@ export const useGetConsensusAccountsAddressDelegationsTo: typeof generated.useGe return { ...delegation, amount: fromBaseUnits(delegation.amount, consensusDecimals), + shares: fromBaseUnits(delegation.shares, consensusDecimals), layer: Layer.consensus, network, ticker, From 5098ceeff375f5983847f2b2faeb084bbdc2e31a Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Tue, 5 Mar 2024 16:27:56 +0100 Subject: [PATCH 09/12] Fix HMR in delegators and undelegations tabs --- .../ValidatorDetailsPage/DebondingDelegationsCard.tsx | 5 ++--- src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx | 5 ++--- src/app/pages/ValidatorDetailsPage/index.tsx | 7 +++---- src/app/pages/ValidatorDetailsPage/tabAnchors.ts | 2 ++ 4 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 src/app/pages/ValidatorDetailsPage/tabAnchors.ts diff --git a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx index 80fe2de96..80ef4f17e 100644 --- a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx +++ b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx @@ -11,15 +11,14 @@ import { CardEmptyState } from '../../components/CardEmptyState' import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' import { Delegations } from '../../components/Delegations' import { ValidatorDetailsContext } from './hooks' - -export const validatorDebondingContainerId = 'debonding' +import { debondingContainerId } from './tabAnchors' export const DebondingDelegationsCard: FC = ({ scope, address }) => { const { t } = useTranslation() return ( - + diff --git a/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx b/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx index 990f56809..4a00c93e2 100644 --- a/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx +++ b/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx @@ -11,15 +11,14 @@ import { CardEmptyState } from '../../components/CardEmptyState' import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' import { Delegations } from '../../components/Delegations' import { ValidatorDetailsContext } from './hooks' - -export const validatorDelegatorsContainerId = 'delegators' +import { delegatorsContainerId } from './tabAnchors' export const DelegatorsCard: FC = ({ scope, address }) => { const { t } = useTranslation() return ( - + diff --git a/src/app/pages/ValidatorDetailsPage/index.tsx b/src/app/pages/ValidatorDetailsPage/index.tsx index 608d0dcfd..8bb945b45 100644 --- a/src/app/pages/ValidatorDetailsPage/index.tsx +++ b/src/app/pages/ValidatorDetailsPage/index.tsx @@ -24,8 +24,7 @@ import { SignedBlocks } from './SignedBlocks' import { StakingTrend } from './StakingTrend' import { ProposedBlocks } from './ProposedBlocks' import { ValidatorDetailsContext } from './hooks' -import { validatorDebondingContainerId } from './DebondingDelegationsCard' -import { validatorDelegatorsContainerId } from './DelegatorsCard' +import { debondingContainerId, delegatorsContainerId } from './tabAnchors' export const StyledGrid = styled(Grid)(({ theme }) => ({ [theme.breakpoints.up('sm')]: { @@ -43,8 +42,8 @@ export const ValidatorDetailsPage: FC = () => { const { isLoading, data } = validatorQuery const validator = data?.data const transactionsLink = useHref('') - const delegatorsLink = useHref(`delegators#${validatorDelegatorsContainerId}`) - const debondingDelegationsLink = useHref(`debonding-delegations#${validatorDebondingContainerId}`) + const delegatorsLink = useHref(`delegators#${delegatorsContainerId}`) + const debondingDelegationsLink = useHref(`debonding-delegations#${debondingContainerId}`) const context: ValidatorDetailsContext = { scope, address } return ( diff --git a/src/app/pages/ValidatorDetailsPage/tabAnchors.ts b/src/app/pages/ValidatorDetailsPage/tabAnchors.ts new file mode 100644 index 000000000..f52eedb6e --- /dev/null +++ b/src/app/pages/ValidatorDetailsPage/tabAnchors.ts @@ -0,0 +1,2 @@ +export const delegatorsContainerId = 'delegators' +export const debondingContainerId = 'debonding' From 556f7a3c938308f35762c7eb79f2f9a705974b11 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Wed, 13 Mar 2024 11:05:20 +0100 Subject: [PATCH 10/12] Handle different link types in delegations component --- src/app/components/Delegations/index.tsx | 12 ++++++++++-- .../pages/ConsensusAccountDetailsPage/Staking.tsx | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app/components/Delegations/index.tsx b/src/app/components/Delegations/index.tsx index 75049d317..460d9f85f 100644 --- a/src/app/components/Delegations/index.tsx +++ b/src/app/components/Delegations/index.tsx @@ -4,6 +4,7 @@ import { DebondingDelegation, Delegation } from '../../../oasis-nexus/api' import { Table, TableCellAlign, TableColProps } from '../Table' import { TablePaginationProps } from '../Table/TablePagination' import { RoundedBalance } from '../RoundedBalance' +import { ValidatorLink } from '../Validators/ValidatorLink' import { AccountLink } from '../Account/AccountLink' type DelegationsProps = { @@ -11,6 +12,7 @@ type DelegationsProps = { delegations?: Delegation[] | DebondingDelegation[] isLoading: boolean limit: number + linkType?: 'delegator' | 'validator' pagination: false | TablePaginationProps } @@ -19,6 +21,7 @@ export const Delegations: FC = ({ delegations, isLoading, limit, + linkType, pagination, }) => { const { t } = useTranslation() @@ -32,10 +35,15 @@ export const Delegations: FC = ({ : []), ] const tableRows = delegations?.map(delegation => ({ - key: delegation.delegator, + key: linkType === 'validator' ? delegation.validator : delegation.delegator, data: [ { - content: , + content: + linkType === 'validator' ? ( + + ) : ( + + ), key: 'delegator', }, { diff --git a/src/app/pages/ConsensusAccountDetailsPage/Staking.tsx b/src/app/pages/ConsensusAccountDetailsPage/Staking.tsx index 44fb4875d..f222521c9 100644 --- a/src/app/pages/ConsensusAccountDetailsPage/Staking.tsx +++ b/src/app/pages/ConsensusAccountDetailsPage/Staking.tsx @@ -81,6 +81,7 @@ const StakingContent: FC = ({ address }) => { delegations={delegationsQuery.data?.data.delegations} isLoading={isLoading} limit={PAGE_SIZE} + linkType="validator" pagination={{ selectedPage: pagination.selectedPage, linkToPage: pagination.linkToPage, From da4ee63b537c219c944ded538471818548217b59 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Mon, 25 Mar 2024 09:17:14 +0100 Subject: [PATCH 11/12] Format shares values --- src/app/components/Delegations/index.tsx | 2 +- src/oasis-nexus/api.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/components/Delegations/index.tsx b/src/app/components/Delegations/index.tsx index 460d9f85f..ea0efb359 100644 --- a/src/app/components/Delegations/index.tsx +++ b/src/app/components/Delegations/index.tsx @@ -53,7 +53,7 @@ export const Delegations: FC = ({ }, { align: TableCellAlign.Right, - content: , + content: , key: 'shares', }, ...(debonding diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index beb8221dc..640b4886c 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -977,7 +977,6 @@ export const useGetConsensusAccountsAddressDelegations: typeof generated.useGetC delegations: data.delegations.map(delegation => { return { ...delegation, - shares: fromBaseUnits(delegation.shares, consensusDecimals), amount: fromBaseUnits(delegation.amount, consensusDecimals), layer: Layer.consensus, network, @@ -1009,7 +1008,6 @@ export const useGetConsensusAccountsAddressDebondingDelegationsTo: typeof genera return { ...delegation, amount: fromBaseUnits(delegation.amount, consensusDecimals), - shares: fromBaseUnits(delegation.shares, consensusDecimals), layer: Layer.consensus, network, ticker, @@ -1040,7 +1038,6 @@ export const useGetConsensusAccountsAddressDelegationsTo: typeof generated.useGe return { ...delegation, amount: fromBaseUnits(delegation.amount, consensusDecimals), - shares: fromBaseUnits(delegation.shares, consensusDecimals), layer: Layer.consensus, network, ticker, From ec17e1103b268ed873aecd293aca27e7cb2d2e96 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Wed, 27 Mar 2024 11:05:49 +0100 Subject: [PATCH 12/12] Move fetching out of view components --- .../DebondingDelegationsCard.tsx | 56 ++++++++++++++----- .../ValidatorDetailsPage/DelegatorsCard.tsx | 48 +++++++++++----- 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx index 80ef4f17e..ef8ec8f3a 100644 --- a/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx +++ b/src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx @@ -3,7 +3,10 @@ import { useTranslation } from 'react-i18next' import Card from '@mui/material/Card' import CardHeader from '@mui/material/CardHeader' import CardContent from '@mui/material/CardContent' -import { useGetConsensusAccountsAddressDebondingDelegationsTo } from '../../../oasis-nexus/api' +import { + DebondingDelegation, + useGetConsensusAccountsAddressDebondingDelegationsTo, +} from '../../../oasis-nexus/api' import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE as limit } from '../../config' import { ErrorBoundary } from '../../components/ErrorBoundary' import { LinkableDiv } from '../../components/PageLayout/LinkableDiv' @@ -12,9 +15,19 @@ import { useSearchParamsPagination } from '../../components/Table/useSearchParam import { Delegations } from '../../components/Delegations' import { ValidatorDetailsContext } from './hooks' import { debondingContainerId } from './tabAnchors' +import { SimplePaginationEngine } from 'app/components/Table/PaginationEngine' export const DebondingDelegationsCard: FC = ({ scope, address }) => { const { t } = useTranslation() + const { network } = scope + const pagination = useSearchParamsPagination('page') + const offset = (pagination.selectedPage - 1) * limit + const debondingQuery = useGetConsensusAccountsAddressDebondingDelegationsTo(network, address, { + limit, + offset, + }) + const { isFetched, isLoading, data } = debondingQuery + const debondingDelegations = data?.data.debonding_delegations return ( @@ -23,24 +36,39 @@ export const DebondingDelegationsCard: FC = ({ scope, a - + ) } -const DebondingDelegationsView: FC = ({ scope, address }) => { +type DebondingDelegationsViewProps = { + debondingDelegations: DebondingDelegation[] | undefined + isFetched: boolean + isLoading: boolean + pagination: SimplePaginationEngine & { + isTotalCountClipped: boolean | undefined + totalCount: number | undefined + } +} + +const DebondingDelegationsView: FC = ({ + debondingDelegations, + isFetched, + isLoading, + pagination, +}) => { const { t } = useTranslation() - const { network } = scope - const pagination = useSearchParamsPagination('page') - const offset = (pagination.selectedPage - 1) * limit - const debondingQuery = useGetConsensusAccountsAddressDebondingDelegationsTo(network, address, { - limit, - offset, - }) - const { isFetched, isLoading, data } = debondingQuery - const debondingDelegations = data?.data.debonding_delegations return ( <> @@ -55,8 +83,8 @@ const DebondingDelegationsView: FC = ({ scope, address pagination={{ selectedPage: pagination.selectedPage, linkToPage: pagination.linkToPage, - totalCount: data?.data.total_count, - isTotalCountClipped: data?.data.is_total_count_clipped, + totalCount: pagination.totalCount, + isTotalCountClipped: pagination.isTotalCountClipped, rowsPerPage: limit, }} /> diff --git a/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx b/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx index 4a00c93e2..75e0f0602 100644 --- a/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx +++ b/src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import Card from '@mui/material/Card' import CardHeader from '@mui/material/CardHeader' import CardContent from '@mui/material/CardContent' -import { useGetConsensusAccountsAddressDelegationsTo } from '../../../oasis-nexus/api' +import { Delegation, useGetConsensusAccountsAddressDelegationsTo } from '../../../oasis-nexus/api' import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE as limit } from '../../config' import { ErrorBoundary } from '../../components/ErrorBoundary' import { LinkableDiv } from '../../components/PageLayout/LinkableDiv' @@ -12,9 +12,19 @@ import { useSearchParamsPagination } from '../../components/Table/useSearchParam import { Delegations } from '../../components/Delegations' import { ValidatorDetailsContext } from './hooks' import { delegatorsContainerId } from './tabAnchors' +import { SimplePaginationEngine } from 'app/components/Table/PaginationEngine' export const DelegatorsCard: FC = ({ scope, address }) => { const { t } = useTranslation() + const { network } = scope + const pagination = useSearchParamsPagination('page') + const offset = (pagination.selectedPage - 1) * limit + const delegationsQuery = useGetConsensusAccountsAddressDelegationsTo(network, address, { + limit, + offset, + }) + const { isFetched, isLoading, data } = delegationsQuery + const delegations = data?.data.delegations return ( @@ -23,24 +33,34 @@ export const DelegatorsCard: FC = ({ scope, address }) - + ) } -const DelegatorsCardView: FC = ({ scope, address }) => { +type DelegatorsViewProps = { + delegations: Delegation[] | undefined + isFetched: boolean + isLoading: boolean + pagination: SimplePaginationEngine & { + isTotalCountClipped: boolean | undefined + totalCount: number | undefined + } +} + +const DelegatorsCardView: FC = ({ delegations, isFetched, isLoading, pagination }) => { const { t } = useTranslation() - const { network } = scope - const pagination = useSearchParamsPagination('page') - const offset = (pagination.selectedPage - 1) * limit - const delegationsQuery = useGetConsensusAccountsAddressDelegationsTo(network, address, { - limit, - offset, - }) - const { isFetched, isLoading, data } = delegationsQuery - const delegations = data?.data.delegations return ( <> @@ -52,8 +72,8 @@ const DelegatorsCardView: FC = ({ scope, address }) => pagination={{ selectedPage: pagination.selectedPage, linkToPage: pagination.linkToPage, - totalCount: data?.data.total_count, - isTotalCountClipped: data?.data.is_total_count_clipped, + totalCount: pagination.totalCount, + isTotalCountClipped: pagination.isTotalCountClipped, rowsPerPage: limit, }} />