diff --git a/CHANGELOG.md b/CHANGELOG.md index 93668fd14..c0f68cf46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Bug fixes +- [#849](https://github.com/alleslabs/celatone-frontend/pull/849) Fix validator list empty state and voting percent dividers - [#846](https://github.com/alleslabs/celatone-frontend/pull/846) Fix voting period status badge - [#838](https://github.com/alleslabs/celatone-frontend/pull/838) Fix validator list sorting on priority - [#839](https://github.com/alleslabs/celatone-frontend/pull/839) Disable contract delegations on non-gov chains diff --git a/src/config/chain/initia.ts b/src/config/chain/initia.ts index 6710b4d52..a9983a378 100644 --- a/src/config/chain/initia.ts +++ b/src/config/chain/initia.ts @@ -27,7 +27,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { enabled: true, moduleMaxFileSize: 1_048_576, decodeApi: INITIA_DECODER, - verify: "https://compiler.mahalo-1.initia.xyz/contracts/verify", + verify: "https://compiler.mahalo-2.initia.xyz/contracts/verify", }, pool: { enabled: false, diff --git a/src/lib/pages/validator-details/components/validator-top/ValidatorStats.tsx b/src/lib/pages/validator-details/components/validator-top/ValidatorStats.tsx index 62b060e27..e438df63b 100644 --- a/src/lib/pages/validator-details/components/validator-top/ValidatorStats.tsx +++ b/src/lib/pages/validator-details/components/validator-top/ValidatorStats.tsx @@ -6,7 +6,7 @@ import { useValidatorStakingProvisions, } from "lib/services/validatorService"; import type { Option, ValidatorAddr } from "lib/types"; -import { formatPrettyPercent } from "lib/utils"; +import { divWithDefault, formatPrettyPercent } from "lib/utils"; const StatWithLabel = ({ label, @@ -49,15 +49,17 @@ export const ValidatorStats = ({ singleStakingDenom, }: ValidatorStatsProps) => { const { data: stakingProvisions, isLoading: isStakingProvisionsLoading } = - useValidatorStakingProvisions(); + useValidatorStakingProvisions(!!singleStakingDenom); const { data: delegations, isLoading: isDelegationsLoading } = useValidatorDelegators(validatorAddress); const estimatedApr = stakingProvisions ? formatPrettyPercent( - stakingProvisions.stakingProvisions - .div(totalVotingPower.mul(1 - commissionRate)) - .toNumber(), + divWithDefault( + stakingProvisions.stakingProvisions, + totalVotingPower.mul(1 - commissionRate), + 0 + ).toNumber(), 2, true ) diff --git a/src/lib/pages/validators/components/ActiveFilter.tsx b/src/lib/pages/validators/components/ActiveFilter.tsx index f8441eca3..7c1660ee3 100644 --- a/src/lib/pages/validators/components/ActiveFilter.tsx +++ b/src/lib/pages/validators/components/ActiveFilter.tsx @@ -1,5 +1,6 @@ import { Flex, Text } from "@chakra-ui/react"; import { Select } from "chakra-react-select"; +import { isUndefined } from "lodash"; import { useMemo } from "react"; import type { Option } from "lib/types"; @@ -12,7 +13,7 @@ interface ActiveFilterProps { } const getOptionLabel = (label: string, count: Option) => - label + (count ? ` (${count})` : ""); + label + (!isUndefined(count) ? ` (${count})` : ""); export const ActiveFilter = ({ isActive, @@ -32,7 +33,7 @@ export const ActiveFilter = ({ ); return ( - + Show only diff --git a/src/lib/pages/validators/components/validators-table/ValidatorsTableBody.tsx b/src/lib/pages/validators/components/validators-table/ValidatorsTableBody.tsx deleted file mode 100644 index dc415ad6e..000000000 --- a/src/lib/pages/validators/components/validators-table/ValidatorsTableBody.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import type { GridProps } from "@chakra-ui/react"; -import React from "react"; - -import { ValidatorOrder } from "../../types"; -import { useCelatoneApp, useMobile } from "lib/app-provider"; -import { Loading } from "lib/components/Loading"; -import { EmptyState, ErrorFetching } from "lib/components/state"; -import { useAssetInfos } from "lib/services/assetService"; -import type { ValidatorsResponse } from "lib/services/validator"; -import type { Option } from "lib/types"; -import { coinToTokenWithValue } from "lib/utils"; - -import { ValidatorsPercentDivider } from "./ValidatorsPercentDivider"; -import { ValidatorsTableMobileCard } from "./ValidatorsTableMobileCard"; -import { ValidatorsTableRow } from "./ValidatorsTableRow"; - -interface ValidatorsTableBodyProps { - templateColumns: GridProps["templateColumns"]; - data: Option; - isLoading: boolean; - isActive: boolean; - order: ValidatorOrder; - isDesc: boolean; -} - -export const ValidatorsTableBody = ({ - templateColumns, - data, - isLoading, - isActive, - order, - isDesc, -}: ValidatorsTableBodyProps) => { - const isMobile = useMobile(); - const { - chainConfig: { - extra: { singleStakingDenom }, - }, - } = useCelatoneApp(); - const { data: assetInfos } = useAssetInfos({ withPrices: false }); - - if (isLoading) return ; - if (!data) return ; - if (!data.total) - return ( - - ); - - const displayDividers = order === ValidatorOrder.VotingPower && isDesc; - const denomToken = singleStakingDenom - ? coinToTokenWithValue(singleStakingDenom, "0", assetInfos) - : undefined; - return isMobile ? ( - <> - {data.items.map((validator) => ( - - ))} - - ) : ( - <> - {data.items.map((validator) => ( - - - {displayDividers && - data.metadata.percent33Rank === validator.rank && ( - - )} - {displayDividers && - data.metadata.percent66Rank === validator.rank && ( - - )} - - ))} - - ); -}; diff --git a/src/lib/pages/validators/components/validators-table/index.tsx b/src/lib/pages/validators/components/validators-table/index.tsx index fc7393401..d51ababe0 100644 --- a/src/lib/pages/validators/components/validators-table/index.tsx +++ b/src/lib/pages/validators/components/validators-table/index.tsx @@ -1,13 +1,20 @@ import { TableContainer } from "@chakra-ui/react"; +import { Fragment } from "react"; -import type { ValidatorOrder } from "../../types"; -import { useMobile } from "lib/app-provider"; +import { ValidatorOrder } from "../../types"; +import { useCelatoneApp, useMobile } from "lib/app-provider"; +import { Loading } from "lib/components/Loading"; +import { EmptyState, ErrorFetching } from "lib/components/state"; import { MobileTableContainer } from "lib/components/table"; +import { useAssetInfos } from "lib/services/assetService"; import type { ValidatorsResponse } from "lib/services/validator"; import type { Option } from "lib/types"; +import { coinToTokenWithValue } from "lib/utils"; -import { ValidatorsTableBody } from "./ValidatorsTableBody"; +import { ValidatorsPercentDivider } from "./ValidatorsPercentDivider"; import { ValidatorsTableHeader } from "./ValidatorsTableHeader"; +import { ValidatorsTableMobileCard } from "./ValidatorsTableMobileCard"; +import { ValidatorsTableRow } from "./ValidatorsTableRow"; interface ValidatorsTableProps { data: Option; @@ -31,19 +38,44 @@ export const ValidatorsTable = ({ scrollComponentId, }: ValidatorsTableProps) => { const isMobile = useMobile(); + const { + chainConfig: { + extra: { singleStakingDenom }, + }, + } = useCelatoneApp(); + const { data: assetInfos } = useAssetInfos({ withPrices: false }); + + if (isLoading) return ; + if (!data) return ; + if (!data.total) + return ( + + ); + + const displayDividers = order === ValidatorOrder.VotingPower && isDesc; + const denomToken = singleStakingDenom + ? coinToTokenWithValue(singleStakingDenom, "0", assetInfos) + : undefined; + const templateColumns = `${isActive ? "64px " : ""}3fr 2fr 110px 110px`; return ( <> {isMobile ? ( - + {data.items.map((validator) => ( + + ))} ) : ( @@ -56,14 +88,30 @@ export const ValidatorsTable = ({ isDesc={isDesc} setIsDesc={setIsDesc} /> - + {data.items.map((validator) => ( + + + {displayDividers && + (validator.rank === data.metadata.percent33Rank || + validator.rank === data.metadata.percent66Rank) && ( + + )} + + ))} )} diff --git a/src/lib/services/validatorService.ts b/src/lib/services/validatorService.ts index e5bce4ca0..dcde2aaf0 100644 --- a/src/lib/services/validatorService.ts +++ b/src/lib/services/validatorService.ts @@ -127,13 +127,14 @@ export const useValidators = ( ); }; -export const useValidatorStakingProvisions = () => { +export const useValidatorStakingProvisions = (enabled: boolean) => { const endpoint = useBaseApiRoute("validators"); return useQuery( [CELATONE_QUERY_KEYS.VALIDATOR_STAKING_PROVISIONS, endpoint], async () => getValidatorStakingProvisions(endpoint), { + enabled, retry: 1, } );