Skip to content

Commit

Permalink
Merge pull request #1310 from oasisprotocol/mz/consensusValidatorTabs
Browse files Browse the repository at this point in the history
Validator delegators and debonding delegations cards
  • Loading branch information
buberdds authored Mar 27, 2024
2 parents 30a9478 + ec17e11 commit a8eb332
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 19 deletions.
1 change: 1 addition & 0 deletions .changelog/1310.trivial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add validator delegators and debonding delegations cards
60 changes: 43 additions & 17 deletions src/app/components/Delegations/index.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,71 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Table, TableCellAlign, TableColProps } from '../../components/Table'
import { Delegation } 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 { ValidatorLink } from '../Validators/ValidatorLink'
import { AccountLink } from '../Account/AccountLink'

type DelegationsProps = {
delegations?: Delegation[]
debonding?: boolean
delegations?: Delegation[] | DebondingDelegation[]
isLoading: boolean
limit: number
linkType?: 'delegator' | 'validator'
pagination: false | TablePaginationProps
}

export const Delegations: FC<DelegationsProps> = ({ delegations, isLoading, limit, pagination }) => {
export const Delegations: FC<DelegationsProps> = ({
debonding,
delegations,
isLoading,
limit,
linkType,
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') },
{ key: 'delegator', content: t('common.address') },
{ key: 'amount', content: t('validator.amount'), align: TableCellAlign.Right },
{ key: 'shares', content: t('validator.shares'), align: TableCellAlign.Right },
...(debonding
? [{ key: 'debondingEnd', content: t('validator.debondingEnd'), align: TableCellAlign.Right }]
: []),
]
const tableRows = delegations?.map(delegation => ({
key: delegation.validator,
key: linkType === 'validator' ? delegation.validator : delegation.delegator,
data: [
{
// TODO: Use trimmed name when API is updated
content: <ValidatorLink address={delegation.validator} alwaysTrim network={delegation.network} />,
key: 'name',
content:
linkType === 'validator' ? (
<ValidatorLink address={delegation.validator} alwaysTrim network={delegation.network} />
) : (
<AccountLink scope={delegation} address={delegation.delegator} />
),
key: 'delegator',
},
{
align: TableCellAlign.Right,
content: <RoundedBalance value={delegation.shares} />,
key: 'shares',
content: <RoundedBalance value={delegation.amount} />,
key: 'amount',
},
{
align: TableCellAlign.Right,

content: <RoundedBalance value={delegation.amount} ticker={delegation.ticker} />,
key: 'staked',
content: <RoundedBalance compactLargeNumbers value={delegation.shares} />,
key: 'shares',
},
...(debonding
? [
{
align: TableCellAlign.Right,
// TODO: Add when API returns correct value and provides current epoch
content: <>-</>,
key: 'debondingEnd',
},
]
: []),
],
}))

Expand All @@ -48,7 +74,7 @@ export const Delegations: FC<DelegationsProps> = ({ delegations, isLoading, limi
columns={tableColumns}
rows={tableRows}
rowsNumber={limit}
name={t('common.staking')}
name={debonding ? t('validator.undelegations') : t('validator.delegators')}
isLoading={isLoading}
pagination={pagination}
/>
Expand Down
1 change: 1 addition & 0 deletions src/app/pages/ConsensusAccountDetailsPage/Staking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const StakingContent: FC<StakingContentProps> = ({ address }) => {
delegations={delegationsQuery.data?.data.delegations}
isLoading={isLoading}
limit={PAGE_SIZE}
linkType="validator"
pagination={{
selectedPage: pagination.selectedPage,
linkToPage: pagination.linkToPage,
Expand Down
93 changes: 93 additions & 0 deletions src/app/pages/ValidatorDetailsPage/DebondingDelegationsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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 {
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'
import { CardEmptyState } from '../../components/CardEmptyState'
import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination'
import { Delegations } from '../../components/Delegations'
import { ValidatorDetailsContext } from './hooks'
import { debondingContainerId } from './tabAnchors'
import { SimplePaginationEngine } from 'app/components/Table/PaginationEngine'

export const DebondingDelegationsCard: FC<ValidatorDetailsContext> = ({ 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 (
<Card>
<LinkableDiv id={debondingContainerId}>
<CardHeader disableTypography component="h3" title={t('validator.undelegations')} />
</LinkableDiv>
<CardContent>
<ErrorBoundary light={true}>
<DebondingDelegationsView
debondingDelegations={debondingDelegations}
isFetched={isFetched}
isLoading={isLoading}
pagination={{
...pagination,
isTotalCountClipped: data?.data.is_total_count_clipped,
totalCount: data?.data.total_count,
}}
/>
</ErrorBoundary>
</CardContent>
</Card>
)
}

type DebondingDelegationsViewProps = {
debondingDelegations: DebondingDelegation[] | undefined
isFetched: boolean
isLoading: boolean
pagination: SimplePaginationEngine & {
isTotalCountClipped: boolean | undefined
totalCount: number | undefined
}
}

const DebondingDelegationsView: FC<DebondingDelegationsViewProps> = ({
debondingDelegations,
isFetched,
isLoading,
pagination,
}) => {
const { t } = useTranslation()

return (
<>
{isFetched && !debondingDelegations?.length && (
<CardEmptyState label={t('validator.emptyDebondingList')} />
)}
<Delegations
debonding
delegations={debondingDelegations}
isLoading={isLoading}
limit={limit}
pagination={{
selectedPage: pagination.selectedPage,
linkToPage: pagination.linkToPage,
totalCount: pagination.totalCount,
isTotalCountClipped: pagination.isTotalCountClipped,
rowsPerPage: limit,
}}
/>
</>
)
}
82 changes: 82 additions & 0 deletions src/app/pages/ValidatorDetailsPage/DelegatorsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
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 { 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'
import { CardEmptyState } from '../../components/CardEmptyState'
import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination'
import { Delegations } from '../../components/Delegations'
import { ValidatorDetailsContext } from './hooks'
import { delegatorsContainerId } from './tabAnchors'
import { SimplePaginationEngine } from 'app/components/Table/PaginationEngine'

export const DelegatorsCard: FC<ValidatorDetailsContext> = ({ 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 (
<Card>
<LinkableDiv id={delegatorsContainerId}>
<CardHeader disableTypography component="h3" title={t('validator.delegators')} />
</LinkableDiv>
<CardContent>
<ErrorBoundary light={true}>
<DelegatorsCardView
delegations={delegations}
isFetched={isFetched}
isLoading={isLoading}
pagination={{
...pagination,
isTotalCountClipped: data?.data.is_total_count_clipped,
totalCount: data?.data.total_count,
}}
/>
</ErrorBoundary>
</CardContent>
</Card>
)
}

type DelegatorsViewProps = {
delegations: Delegation[] | undefined
isFetched: boolean
isLoading: boolean
pagination: SimplePaginationEngine & {
isTotalCountClipped: boolean | undefined
totalCount: number | undefined
}
}

const DelegatorsCardView: FC<DelegatorsViewProps> = ({ delegations, isFetched, isLoading, pagination }) => {
const { t } = useTranslation()

return (
<>
{isFetched && !delegations?.length && <CardEmptyState label={t('validator.emptyDelegationsList')} />}
<Delegations
delegations={delegations}
isLoading={isLoading}
limit={limit}
pagination={{
selectedPage: pagination.selectedPage,
linkToPage: pagination.linkToPage,
totalCount: pagination.totalCount,
isTotalCountClipped: pagination.isTotalCountClipped,
rowsPerPage: limit,
}}
/>
</>
)
}
12 changes: 11 additions & 1 deletion src/app/pages/ValidatorDetailsPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { SignedBlocks } from './SignedBlocks'
import { StakingTrend } from './StakingTrend'
import { ProposedBlocks } from './ProposedBlocks'
import { ValidatorDetailsContext } from './hooks'
import { debondingContainerId, delegatorsContainerId } from './tabAnchors'

export const StyledGrid = styled(Grid)(({ theme }) => ({
[theme.breakpoints.up('sm')]: {
Expand All @@ -41,6 +42,8 @@ export const ValidatorDetailsPage: FC = () => {
const { isLoading, data } = validatorQuery
const validator = data?.data
const transactionsLink = useHref('')
const delegatorsLink = useHref(`delegators#${delegatorsContainerId}`)
const debondingDelegationsLink = useHref(`debonding-delegations#${debondingContainerId}`)
const context: ValidatorDetailsContext = { scope, address }

return (
Expand All @@ -58,7 +61,14 @@ export const ValidatorDetailsPage: FC = () => {
</StyledGrid>
</Grid>
<ProposedBlocks scope={scope} />
<RouterTabs tabs={[{ label: t('common.transactions'), to: transactionsLink }]} context={context} />
<RouterTabs
tabs={[
{ label: t('common.transactions'), to: transactionsLink },
{ label: t('validator.delegators'), to: delegatorsLink },
{ label: t('validator.undelegations'), to: debondingDelegationsLink },
]}
context={context}
/>
</PageLayout>
)
}
Expand Down
2 changes: 2 additions & 0 deletions src/app/pages/ValidatorDetailsPage/tabAnchors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const delegatorsContainerId = 'delegators'
export const debondingContainerId = 'debonding'
5 changes: 5 additions & 0 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@
},
"validator": {
"active": "Active",
"amount": "Amount",
"balanceDistribution": "Balance Distribution",
"blockWithHeight": "Block {{height}}",
"boundsNotSet": "No bounds set (0% - 100%)",
Expand All @@ -608,7 +609,10 @@
"commissionBounds": "Commission Bounds",
"commissionRates": "Commission Rates",
"cumulativeVoting": "Cumulative Voting %",
"debondingEnd": "End of debonding",
"delegators": "Delegators",
"emptyDebondingList": "No one is debonding delegations from this validator.",
"emptyDelegationsList": "No one is delegating to this validator.",
"entityId": "Entity ID",
"externalLink": "External Link",
"externalLinkDescriotion": "Oasis is not responsible for any content on
external pages.",
Expand All @@ -630,6 +634,7 @@
"startDate": "Start Date",
"title": "Validator",
"totalShare": "Total Share",
"undelegations": "Undelegations",
"uptime": "Uptime",
"voting": "Voting %",
"votingPower": "Voting Power"
Expand Down
Loading

0 comments on commit a8eb332

Please sign in to comment.