From 4f1a270b9c151b3ef4ec9fc157a96b3b903f4a40 Mon Sep 17 00:00:00 2001 From: selankon Date: Mon, 29 Jul 2024 11:18:17 +0200 Subject: [PATCH 1/8] Implement validators detail page --- src/components/Validators/Detail.tsx | 123 ++++++++++++++++++++ src/components/Validators/ValidatorCard.tsx | 63 +++++----- src/constants.ts | 1 + src/pages/validator.tsx | 19 +++ src/router/index.tsx | 10 ++ 5 files changed, 187 insertions(+), 29 deletions(-) create mode 100644 src/components/Validators/Detail.tsx create mode 100644 src/pages/validator.tsx diff --git a/src/components/Validators/Detail.tsx b/src/components/Validators/Detail.tsx new file mode 100644 index 0000000..1df5021 --- /dev/null +++ b/src/components/Validators/Detail.tsx @@ -0,0 +1,123 @@ +import { Button, Flex, Heading, HStack, Tab, TabList, TabPanel, TabPanels, Text, VStack } from '@chakra-ui/react' +import { ensure0x, IChainValidator } from '@vocdoni/sdk' +import { Trans, useTranslation } from 'react-i18next' +import { DetailsGrid, GridItemProps } from '~components/Layout/DetailsGrid' +import { QueryParamsTabs } from '~components/Layout/QueryParamsTabs' +import { RawContentBox } from '~components/Layout/ShowRawButton' +import { ReducedTextAndCopy } from '~components/Layout/CopyButton' +import { HiOutlineCube } from 'react-icons/hi2' +import { RoutePath } from '~constants' +import { generatePath, Link as RouterLink } from 'react-router-dom' + +export type ValidatorFixedType = IChainValidator & { + // todo(kon): delete this type extension when https://github.com/vocdoni/vocdoni-sdk/pull/402 is merged + joinHeight: number + proposals: number + score: number + validatorAddress: string + votes: number +} + +const DetailsTab = ({ validator }: { validator: ValidatorFixedType }) => { + const address = ensure0x(validator.address) + const pubKey = ensure0x(validator.pubKey) + + const { t } = useTranslation() + + const details: GridItemProps[] = [ + { + label: t('validators.pubKey', { defaultValue: 'Public Key' }), + children: ( + + {pubKey} + + ), + }, + { + label: t('validators.voting_power_cell', { defaultValue: 'Voting power' }), + children: validator.power, + }, + { + label: t('validators.score', { defaultValue: 'Score' }), + children: validator.score, + }, + { + label: t('validators.votes', { defaultValue: 'Votes' }), + children: validator.votes, + }, + ] + + return ( + + + + ) +} + +export const ValidatorDetail = ({ validator }: { validator: ValidatorFixedType }) => { + const validatorAddress = ensure0x(validator.validatorAddress) + const name = validator.name + + return ( + + + + Validator Details + + + + {validatorAddress} + + {!!name && ({name})} + + + + Joint on block + + + + + + + + Details + + + Raw + + + + + + + + + + + + + ) +} diff --git a/src/components/Validators/ValidatorCard.tsx b/src/components/Validators/ValidatorCard.tsx index 1188183..50bf70c 100644 --- a/src/components/Validators/ValidatorCard.tsx +++ b/src/components/Validators/ValidatorCard.tsx @@ -1,40 +1,45 @@ -import { Card, CardBody, Flex, HStack, Text } from '@chakra-ui/react' -import { ensure0x, IChainValidator } from '@vocdoni/sdk' +import { Card, CardBody, Flex, HStack, Link, Text } from '@chakra-ui/react' +import { ensure0x } from '@vocdoni/sdk' import { Trans } from 'react-i18next' import { ReducedTextAndCopy } from '~components/Layout/CopyButton' +import { generatePath, Link as RouterLink } from 'react-router-dom' +import { RoutePath } from '~constants' +import { ValidatorFixedType } from '~components/Validators/Detail' -export const ValidatorCard = (validator: IChainValidator) => { +export const ValidatorCard = (validator: ValidatorFixedType) => { return ( - - - - {ensure0x(validator.address)} - - - + + + + + {validator.name && `(${validator.name})`} {ensure0x(validator.validatorAddress)} + + + + + PubKey: + + + {validator.pubKey} + + - PubKey: + + Voting power: {{ power: validator.power }} + - - {validator.pubKey} - - - - - Voting power: {{ power: validator.power }} - - + - - + + ) } diff --git a/src/constants.ts b/src/constants.ts index 3320323..4b6507e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -30,6 +30,7 @@ export enum RoutePath { Transaction = '/transactions/:block/:index', TransactionsList = '/transactions/:page?', TransactionByHashOrHeight = '/transactions/:hashOrHeight', + Validator = '/validator/:address', Validators = '/validators', Verify = '/verify/:verifier?', } diff --git a/src/pages/validator.tsx b/src/pages/validator.tsx new file mode 100644 index 0000000..5ac56c0 --- /dev/null +++ b/src/pages/validator.tsx @@ -0,0 +1,19 @@ +import { useLoaderData, useParams } from 'react-router-dom' +import { ensure0x, IChainValidatorsListResponse } from '@vocdoni/sdk' +import { ValidatorDetail, ValidatorFixedType } from '~components/Validators/Detail' + +const Validator = () => { + const validators = (useLoaderData() as IChainValidatorsListResponse).validators as Array + + const { address }: { address?: string } = useParams() + + const validator = validators.find( + (v) => ensure0x(v.validatorAddress.toLowerCase()) === ensure0x(address?.toLowerCase() ?? '') + ) + + if (!address || !validator) throw new Error('Validator not found') + + return +} + +export default Validator diff --git a/src/router/index.tsx b/src/router/index.tsx index 197c3ec..4662b60 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -21,6 +21,7 @@ const ProcessList = lazy(() => import('~pages/processes')) const Process = lazy(() => import('~pages/process')) const Transaction = lazy(() => import('~pages/transaction')) const TransactionsList = lazy(() => import('~pages/transactions')) +const Validator = lazy(() => import('~pages/validator')) const Validators = lazy(() => import('~pages/validators')) const Verify = lazy(() => import('~pages/verify')) @@ -145,6 +146,15 @@ export const RoutesProvider = () => { return await client.txInfoByBlock(Number(tx.blockHeight), Number(tx.transactionIndex)) }, }, + { + path: RoutePath.Validator, + element: ( + + + + ), + loader: async ({ params }) => await client.validatorsList(), + }, { path: RoutePath.Validators, element: ( From f655b7092696d8994bb05f127f3362c5c195898b Mon Sep 17 00:00:00 2001 From: selankon Date: Mon, 29 Jul 2024 11:56:37 +0200 Subject: [PATCH 2/8] Create IconLink --- src/components/Layout/IconLink.tsx | 34 ++++++++++++++++++++++++++++ src/components/Validators/Detail.tsx | 20 ++++------------ 2 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 src/components/Layout/IconLink.tsx diff --git a/src/components/Layout/IconLink.tsx b/src/components/Layout/IconLink.tsx new file mode 100644 index 0000000..9b1ebd7 --- /dev/null +++ b/src/components/Layout/IconLink.tsx @@ -0,0 +1,34 @@ +import { Flex, Icon, IconProps, Link } from '@chakra-ui/react' +import { generatePath, Link as RouterLink } from 'react-router-dom' +import { PropsWithChildren } from 'react' +import { RoutePath } from '~constants' +import { HiOutlineCube } from 'react-icons/hi2' +import { IconType } from 'react-icons' + +export const BlockIconLink = ({ height }: { height: number }) => ( + + {height} + +) + +type IconLinkProps = { + to: string + icon: IconType +} & IconProps + +export const IconLink = ({ to, icon, children, ...iconProps }: IconLinkProps & PropsWithChildren) => { + return ( + + + + {children} + + + ) +} diff --git a/src/components/Validators/Detail.tsx b/src/components/Validators/Detail.tsx index 1df5021..8b207e6 100644 --- a/src/components/Validators/Detail.tsx +++ b/src/components/Validators/Detail.tsx @@ -1,13 +1,11 @@ -import { Button, Flex, Heading, HStack, Tab, TabList, TabPanel, TabPanels, Text, VStack } from '@chakra-ui/react' +import { Flex, Heading, HStack, Tab, TabList, TabPanel, TabPanels, Text, VStack } from '@chakra-ui/react' import { ensure0x, IChainValidator } from '@vocdoni/sdk' import { Trans, useTranslation } from 'react-i18next' import { DetailsGrid, GridItemProps } from '~components/Layout/DetailsGrid' import { QueryParamsTabs } from '~components/Layout/QueryParamsTabs' import { RawContentBox } from '~components/Layout/ShowRawButton' import { ReducedTextAndCopy } from '~components/Layout/CopyButton' -import { HiOutlineCube } from 'react-icons/hi2' -import { RoutePath } from '~constants' -import { generatePath, Link as RouterLink } from 'react-router-dom' +import { BlockIconLink } from '~components/Layout/IconLink' export type ValidatorFixedType = IChainValidator & { // todo(kon): delete this type extension when https://github.com/vocdoni/vocdoni-sdk/pull/402 is merged @@ -85,19 +83,11 @@ export const ValidatorDetail = ({ validator }: { validator: ValidatorFixedType } {!!name && ({name})} - - + + Joint on block - + From a1614da774745086a1827a2460f1f44eae1829c7 Mon Sep 17 00:00:00 2001 From: selankon Date: Mon, 29 Jul 2024 12:00:45 +0200 Subject: [PATCH 3/8] Use validator link on block --- src/components/Blocks/Detail.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Blocks/Detail.tsx b/src/components/Blocks/Detail.tsx index b3f7dbc..5104fcc 100644 --- a/src/components/Blocks/Detail.tsx +++ b/src/components/Blocks/Detail.tsx @@ -95,6 +95,7 @@ const DetailsTab = ({ block }: { block: IChainBlockInfoResponse }) => { fontWeight={'normal'} h={0} fontSize={'md'} + to={generatePath(RoutePath.Validator, { address: proposer })} > {proposer} From a2dfa663641c21af1ce4f2a7df045f0cdf8594ba Mon Sep 17 00:00:00 2001 From: selankon Date: Mon, 29 Jul 2024 12:05:02 +0200 Subject: [PATCH 4/8] Add todo --- src/pages/validators.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/validators.tsx b/src/pages/validators.tsx index 216b2ae..ebf96e7 100644 --- a/src/pages/validators.tsx +++ b/src/pages/validators.tsx @@ -14,6 +14,8 @@ const Validators = () => { return ( {validators.map((validator, i) => ( + // todo(kon): remove this ignore when https://github.com/vocdoni/vocdoni-sdk/pull/402 is merged + // @ts-ignore ))} From efcf103d8e01036c5e6067f20bca25a5a5f57044 Mon Sep 17 00:00:00 2001 From: selankon Date: Wed, 31 Jul 2024 08:29:18 +0200 Subject: [PATCH 5/8] Extract validator name component --- src/components/Layout/CopyButton.tsx | 12 +++--- src/components/Validators/Detail.tsx | 15 +------ src/components/Validators/ValidatorCard.tsx | 44 ++++++++++++++++++--- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/components/Layout/CopyButton.tsx b/src/components/Layout/CopyButton.tsx index be755a1..fb6e7d7 100644 --- a/src/components/Layout/CopyButton.tsx +++ b/src/components/Layout/CopyButton.tsx @@ -43,6 +43,12 @@ const withCopyLogic = (Component: typeof IconButton | typeof Button) => { export const CopyButton = withCopyLogic(Button) export const CopyButtonIcon = withCopyLogic(IconButton) +export type ReducedTextAndCopyProps = { + breakPoint?: Parameters[0] + to?: string + children?: string +} & ICopyButton + /** * It shows a text with a copy button. * if the length of the string is more than 13 it cut the string to something like 6be21a...0000. @@ -56,11 +62,7 @@ export const ReducedTextAndCopy = ({ to, children = '', ...rest -}: { - to?: string - breakPoint?: Parameters[0] - children?: string -} & ICopyButton) => { +}: ReducedTextAndCopyProps) => { let text = children // If breakpoint is true and the length of the string is more than 13 it shorts the string if (breakPoint && useBreakpointValue(breakPoint) && children.length > 13) { diff --git a/src/components/Validators/Detail.tsx b/src/components/Validators/Detail.tsx index 8b207e6..a5805a7 100644 --- a/src/components/Validators/Detail.tsx +++ b/src/components/Validators/Detail.tsx @@ -6,6 +6,7 @@ import { QueryParamsTabs } from '~components/Layout/QueryParamsTabs' import { RawContentBox } from '~components/Layout/ShowRawButton' import { ReducedTextAndCopy } from '~components/Layout/CopyButton' import { BlockIconLink } from '~components/Layout/IconLink' +import { ValidatorName } from '~components/Validators/ValidatorCard' export type ValidatorFixedType = IChainValidator & { // todo(kon): delete this type extension when https://github.com/vocdoni/vocdoni-sdk/pull/402 is merged @@ -70,19 +71,7 @@ export const ValidatorDetail = ({ validator }: { validator: ValidatorFixedType } Validator Details - - - {validatorAddress} - - {!!name && ({name})} - + Joint on block diff --git a/src/components/Validators/ValidatorCard.tsx b/src/components/Validators/ValidatorCard.tsx index 50bf70c..be248fb 100644 --- a/src/components/Validators/ValidatorCard.tsx +++ b/src/components/Validators/ValidatorCard.tsx @@ -1,20 +1,52 @@ -import { Card, CardBody, Flex, HStack, Link, Text } from '@chakra-ui/react' -import { ensure0x } from '@vocdoni/sdk' +import { Card, CardBody, Flex, HStack, Link, Text, TextProps } from '@chakra-ui/react' import { Trans } from 'react-i18next' -import { ReducedTextAndCopy } from '~components/Layout/CopyButton' +import { ReducedTextAndCopy, ReducedTextAndCopyProps } from '~components/Layout/CopyButton' import { generatePath, Link as RouterLink } from 'react-router-dom' import { RoutePath } from '~constants' import { ValidatorFixedType } from '~components/Validators/Detail' +export const ValidatorName = ({ + name, + address, + nameProps, + addressProps, +}: { + name?: string + address: string + nameProps?: TextProps + addressProps?: Omit +}) => { + const showName = !!name + return ( + + {showName && ( + + {name} + + )} + + {address} + + + ) +} + export const ValidatorCard = (validator: ValidatorFixedType) => { return ( - - {validator.name && `(${validator.name})`} {ensure0x(validator.validatorAddress)} - + From dfc78c3147dbffd142edb23fd4b5348e5b326064 Mon Sep 17 00:00:00 2001 From: selankon Date: Wed, 31 Jul 2024 08:34:32 +0200 Subject: [PATCH 6/8] Fix typo --- src/components/Validators/Detail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Validators/Detail.tsx b/src/components/Validators/Detail.tsx index a5805a7..fbd0b9b 100644 --- a/src/components/Validators/Detail.tsx +++ b/src/components/Validators/Detail.tsx @@ -74,7 +74,7 @@ export const ValidatorDetail = ({ validator }: { validator: ValidatorFixedType } - Joint on block + Joined on block From 92589d2500aab9ba1a04839d104b226dc3d36aa8 Mon Sep 17 00:00:00 2001 From: selankon Date: Wed, 31 Jul 2024 08:37:22 +0200 Subject: [PATCH 7/8] Add address to details --- src/components/Validators/Detail.tsx | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/Validators/Detail.tsx b/src/components/Validators/Detail.tsx index fbd0b9b..e7510ac 100644 --- a/src/components/Validators/Detail.tsx +++ b/src/components/Validators/Detail.tsx @@ -7,6 +7,8 @@ import { RawContentBox } from '~components/Layout/ShowRawButton' import { ReducedTextAndCopy } from '~components/Layout/CopyButton' import { BlockIconLink } from '~components/Layout/IconLink' import { ValidatorName } from '~components/Validators/ValidatorCard' +import { generatePath } from 'react-router-dom' +import { RoutePath } from '~constants' export type ValidatorFixedType = IChainValidator & { // todo(kon): delete this type extension when https://github.com/vocdoni/vocdoni-sdk/pull/402 is merged @@ -40,6 +42,23 @@ const DetailsTab = ({ validator }: { validator: ValidatorFixedType }) => { ), }, + { + label: t('validators.secondary_address', { defaultValue: 'Address' }), + children: ( + + {address} + + ), + }, { label: t('validators.voting_power_cell', { defaultValue: 'Voting power' }), children: validator.power, @@ -62,9 +81,6 @@ const DetailsTab = ({ validator }: { validator: ValidatorFixedType }) => { } export const ValidatorDetail = ({ validator }: { validator: ValidatorFixedType }) => { - const validatorAddress = ensure0x(validator.validatorAddress) - const name = validator.name - return ( From 7a18c934e7254abbfacb2e4db7d532e7a1b37a0e Mon Sep 17 00:00:00 2001 From: selankon Date: Thu, 1 Aug 2024 12:58:36 +0200 Subject: [PATCH 8/8] Implement useCopy --- src/components/Validators/Detail.tsx | 4 +- src/components/Validators/ValidatorCard.tsx | 41 ++++++++++----------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/components/Validators/Detail.tsx b/src/components/Validators/Detail.tsx index e7510ac..6e8c17d 100644 --- a/src/components/Validators/Detail.tsx +++ b/src/components/Validators/Detail.tsx @@ -43,7 +43,7 @@ const DetailsTab = ({ validator }: { validator: ValidatorFixedType }) => { ), }, { - label: t('validators.secondary_address', { defaultValue: 'Address' }), + label: t('validators.account', { defaultValue: 'Account' }), children: ( Validator Details - + Joined on block diff --git a/src/components/Validators/ValidatorCard.tsx b/src/components/Validators/ValidatorCard.tsx index be248fb..f902dd6 100644 --- a/src/components/Validators/ValidatorCard.tsx +++ b/src/components/Validators/ValidatorCard.tsx @@ -1,29 +1,19 @@ -import { Card, CardBody, Flex, HStack, Link, Text, TextProps } from '@chakra-ui/react' +import { Card, CardBody, Flex, HStack, Link, Text } from '@chakra-ui/react' import { Trans } from 'react-i18next' -import { ReducedTextAndCopy, ReducedTextAndCopyProps } from '~components/Layout/CopyButton' +import { ReducedTextAndCopy } from '~components/Layout/CopyButton' import { generatePath, Link as RouterLink } from 'react-router-dom' import { RoutePath } from '~constants' import { ValidatorFixedType } from '~components/Validators/Detail' -export const ValidatorName = ({ - name, - address, - nameProps, - addressProps, -}: { - name?: string - address: string - nameProps?: TextProps - addressProps?: Omit -}) => { +export const ValidatorName = ({ name, useCopy, address }: { name?: string; useCopy?: boolean; address: string }) => { const showName = !!name - return ( - - {showName && ( - - {name} - - )} + let addrsComponent = ( + + {address} + + ) + if (useCopy) { + addrsComponent = ( {address} + ) + } + return ( + + {showName && ( + + {name} + + )} + {addrsComponent} ) }