Skip to content

Commit

Permalink
Validators list: support search
Browse files Browse the repository at this point in the history
  • Loading branch information
csillag committed May 22, 2024
1 parent 4fcae67 commit a992108
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 26 deletions.
16 changes: 11 additions & 5 deletions src/app/components/Validators/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@ type ValidatorsProps = {
isLoading: boolean
limit: number
pagination: false | TablePaginationProps
highlightedPart?: string
}

export const Validators: FC<ValidatorsProps> = ({ isLoading, limit, pagination, validators }) => {
export const Validators: FC<ValidatorsProps> = ({
isLoading,
limit,
pagination,
validators,
highlightedPart,
}) => {
const { t } = useTranslation()
const { network } = useRequiredScopeParam()

Expand All @@ -35,14 +42,12 @@ export const Validators: FC<ValidatorsProps> = ({ isLoading, limit, pagination,
{ key: 'status', content: t('common.status') },
{ key: 'uptime', content: t('validator.uptime') },
]
const offset = typeof pagination === 'boolean' ? 0 : (pagination.selectedPage - 1) * pagination.rowsPerPage
const tableRows = validators?.map((validator, index) => ({
const tableRows = validators?.map(validator => ({
key: validator.entity_address,
data: [
{
align: TableCellAlign.Center,
// TODO: replace index when rank is implemented in the API
content: <div>{offset + index + 1}</div>,
content: <div>{validator.rank + 1}</div>,
key: 'rank',
},
{
Expand All @@ -57,6 +62,7 @@ export const Validators: FC<ValidatorsProps> = ({ isLoading, limit, pagination,
address={validator.entity_address}
name={validator.media?.name}
network={network}
highlightedPart={highlightedPart}
/>
</Box>
),
Expand Down
37 changes: 27 additions & 10 deletions src/app/pages/ValidatorsPage/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,40 @@ import { useGetConsensusValidators, Validator, ValidatorList } from '../../../oa
import { useClientSidePagination } from '../../components/Table/useClientSidePagination'
import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE } from '../../config'
import { Network } from '../../../types/network'
import { useTypedSearchParam } from '../../hooks/useTypedSearchParam'
import { hasTextMatch } from '../../components/HighlightedText/text-matching'
import { useTranslation } from 'react-i18next'

export const useLoadedValidators = (network: Network, tableView: TableLayout) => {
const pagination = useClientSidePagination<Validator, ValidatorList>({
const useValidatorNameSearch = () => useTypedSearchParam('name', '', { deleteParams: ['page'] })

export const useValidatorFiltering = () => {
const { t } = useTranslation()
const [nameSearchInput, setNameSearchInput] = useValidatorNameSearch()
const namePattern = nameSearchInput.length < 3 ? undefined : nameSearchInput
const nameWarning = !!nameSearchInput && !namePattern ? t('tableSearch.error.tooShort') : undefined
return { nameSearchInput, setNameSearchInput, namePattern, nameWarning }
}

export const useValidatorData = (network: Network, tableView: TableLayout) => {
const { namePattern } = useValidatorFiltering()
const nameFilter = namePattern
? (validator: Validator) => hasTextMatch(validator.media?.name, [namePattern])
: undefined
const pagination = useClientSidePagination<Validator, ValidatorList, [number, boolean]>({
paramName: 'page',
serverPageSize: 1000,
clientPageSize: NUMBER_OF_ITEMS_ON_SEPARATE_PAGE,
transform: (data, r) => [
data,
[r.total_count, r.is_total_count_clipped], // Extract the real number of validators
],
filter: nameFilter,
})
const offset = pagination.offsetForQuery
const { offset, limit } = pagination.paramsForServer
const validatorsQuery = useGetConsensusValidators(network, {
limit: tableView === TableLayout.Vertical ? offset + pagination.limitForQuery : pagination.limitForQuery,
limit: tableView === TableLayout.Vertical ? offset + limit : limit,
offset: tableView === TableLayout.Vertical ? 0 : offset,
})
const { isLoading, isFetched, data } = validatorsQuery
const paginatedResults = pagination.getResults(data?.data)
return {
isLoading,
isFetched,
paginatedResults,
}
return pagination.getResults(isLoading, isFetched, data?.data)
}
34 changes: 24 additions & 10 deletions src/app/pages/ValidatorsPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import { SubPageCard } from '../../components/SubPageCard'

import { AppErrors } from '../../../types/errors'
import { TableLayout, TableLayoutButton } from '../../components/TableLayoutButton'
import { LoadMoreButton } from '../../components/LoadMoreButton'
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
import { Validators } from '../../components/Validators'
import { CardHeaderWithCounter } from '../../components/CardHeaderWithCounter'
import { ValidatorDetailsView } from '../ValidatorDetailsPage'
import { VerticalList } from '../../components/VerticalList'
import { useLoadedValidators } from './hooks'
import { useValidatorFiltering, useValidatorData } from './hooks'
import Box from '@mui/material/Box'
import { TableSearchBar } from '../../components/Search/TableSearchBar'

export const ValidatorsPage: FC = () => {
const [tableView, setTableView] = useState<TableLayout>(TableLayout.Horizontal)
Expand All @@ -22,6 +23,7 @@ export const ValidatorsPage: FC = () => {

const scope = useRequiredScopeParam()
const { network } = scope
const { nameSearchInput, setNameSearchInput, nameWarning, namePattern } = useValidatorFiltering()

useEffect(() => {
if (!isMobile) {
Expand All @@ -31,15 +33,16 @@ export const ValidatorsPage: FC = () => {

const {
isLoading,
isFetched,
paginatedResults,
// pagination, pageSize, isLoading, isFetched, validatorsData
} = useLoadedValidators(network, tableView)
tablePaginationProps,
data: validators,
extractedData,
hasNoResultsOnSelectedPage,
} = useValidatorData(network, tableView)

const { tablePaginationProps, data: validators } = paginatedResults
const { selectedPage, totalCount, isTotalCountClipped, rowsPerPage } = tablePaginationProps
const { rowsPerPage } = tablePaginationProps
const [totalCount, isTotalCountClipped] = extractedData ?? [0, false]

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

Expand All @@ -59,7 +62,17 @@ export const ValidatorsPage: FC = () => {
isTotalCountClipped={isTotalCountClipped}
/>
}
action={isMobile && <TableLayoutButton tableView={tableView} setTableView={setTableView} />}
action={
<Box sx={{ display: 'flex-inline' }}>
<TableSearchBar
value={nameSearchInput}
onChange={setNameSearchInput}
placeholder={t('validator.search')}
warning={nameWarning}
/>
{isMobile && <TableLayoutButton tableView={tableView} setTableView={setTableView} />}
</Box>
}
noPadding={tableView === TableLayout.Vertical}
mainTitle
>
Expand All @@ -69,6 +82,7 @@ export const ValidatorsPage: FC = () => {
isLoading={isLoading}
limit={rowsPerPage}
pagination={tablePaginationProps}
highlightedPart={namePattern}
/>
)}
{tableView === TableLayout.Vertical && (
Expand Down
1 change: 1 addition & 0 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@
"signedBlocks": "Signed Blocks",
"signedBlocksDescription": "Last 100 blocks",
"proposedBlocks": "Proposed Blocks",
"search": "Search by name",
"snapshot": "Validator Snapshot",
"shares": "Shares",
"sharesDocs": "How shares are calculated",
Expand Down
4 changes: 3 additions & 1 deletion src/oasis-nexus/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ declare module './generated/api' {

export interface Validator {
ticker: Ticker
rank: number
}

export interface Proposal {
Expand Down Expand Up @@ -965,10 +966,11 @@ export const useGetConsensusValidators: typeof generated.useGetConsensusValidato
...arrayify(axios.defaults.transformResponse),
(data: generated.ValidatorList, headers, status): ExtendedValidatorList => {
if (status !== 200) return data
const validators = data.validators.map((validator): generated.Validator => {
const validators = data.validators.map((validator, index): generated.Validator => {
return {
...validator,
escrow: fromBaseUnits(validator.escrow, consensusDecimals),
rank: index + (params?.offset ?? 0), // TODO: remove this when rank is added to API
ticker,
}
})
Expand Down

0 comments on commit a992108

Please sign in to comment.