From ff58517e7cec7d12619532a99b9851d9aaaa4713 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Mar 2024 12:33:52 +0700 Subject: [PATCH 1/6] feat: recent 100 blocks chart --- .../performance/RecentBlocksSection.tsx | 174 ++++++++++++++---- .../components/performance/UptimeSection.tsx | 8 +- .../components/performance/index.tsx | 4 +- .../components/validator-overview/index.tsx | 2 +- src/lib/services/validatorService.ts | 1 + 5 files changed, 148 insertions(+), 41 deletions(-) diff --git a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx index adb40f5fb..677b090db 100644 --- a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx +++ b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx @@ -1,55 +1,155 @@ import { Box, Flex, Grid, Heading, Text } from "@chakra-ui/react"; +import { useEffect, useRef } from "react"; -interface RecentBlocksSectionProps { - hasTitle?: boolean; +import { Loading } from "lib/components/Loading"; +import { Tooltip } from "lib/components/Tooltip"; +import { useValidatorUptime } from "lib/services/validatorService"; +import { BlockVote } from "lib/types"; +import type { ValidatorAddr } from "lib/types"; +import { formatUTC } from "lib/utils"; + +interface BlockProps { + height: number; + vote: BlockVote; + withCursor?: boolean; } -const Block = ({ isHighlighted = false }: { isHighlighted: boolean }) => ( - -); +const Block = ({ height, vote, withCursor = false }: BlockProps) => { + let backgroundColor = "primary.main"; + + if (vote === BlockVote.PROPOSE) { + backgroundColor = "secondary.main"; + } else if (vote === BlockVote.ABSTAIN) { + backgroundColor = "error.dark"; + } + + let voteLabel = "Signed"; + + if (vote === BlockVote.PROPOSE) { + voteLabel = "Proposed"; + } else if (vote === BlockVote.ABSTAIN) { + voteLabel = "Missed"; + } + + return ( + + + + {withCursor && ( + + )} + + + ); +}; + +interface RecentBlocksSectionProps { + validatorAddress: ValidatorAddr; +} export const RecentBlocksSection = ({ - hasTitle = false, + validatorAddress, }: RecentBlocksSectionProps) => { - // TODO: remove mock up data - const blocks = new Array(100) - .fill(0) - .map((_, index) => ( - - )); + const { data, isLoading, dataUpdatedAt } = useValidatorUptime( + validatorAddress, + 100 + ); + + const parentRef = useRef(null); + const hoverTextRef = useRef(null); + + useEffect(() => { + const handleResize = () => { + const parentElement = parentRef.current; + const cursorElement = document.getElementById( + "most-recent-100-blocks-cursor" + ); + const hoverTextElement = hoverTextRef.current; + + if (parentElement && cursorElement && hoverTextElement) { + const parentRect = parentElement.getBoundingClientRect(); + const cursorRect = cursorElement.getBoundingClientRect(); + const diffLeft = cursorRect.left - parentRect.left; + const diffRight = parentRect.right - cursorRect.right; + + if (diffLeft < parentRect.width / 2) { + hoverTextElement.style.left = `${diffLeft}px`; + } else { + hoverTextElement.style.right = `${diffRight}px`; + } + } + }; + + window.addEventListener("resize", handleResize); + + // Call the handler right away to set initial state + handleResize(); + + return () => window.removeEventListener("resize", handleResize); + }, [dataUpdatedAt]); + + if (isLoading) return ; return ( - - {hasTitle && ( - - - Most Recent 100 Blocks - - - Latest Update: timestamp - - - )} + + + + Most Recent 100 Blocks + + + Latest Update: {formatUTC(new Date(dataUpdatedAt))} + + - {blocks} + {data?.recent100Blocks + .map((block, index) => ( + + )) + .reverse()} - {/* TODO: add arrow and align with last block */} - - Most Recent Blocks: 12345678 + + Most Recent Blocks: {data?.recent100Blocks[0].height ?? "N/A"} ); diff --git a/src/lib/pages/validator-details/components/performance/UptimeSection.tsx b/src/lib/pages/validator-details/components/performance/UptimeSection.tsx index 3452066d6..86a4d69f6 100644 --- a/src/lib/pages/validator-details/components/performance/UptimeSection.tsx +++ b/src/lib/pages/validator-details/components/performance/UptimeSection.tsx @@ -15,7 +15,7 @@ import { useMobile } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { ValueWithIcon } from "lib/components/ValueWithIcon"; import type { ValidatorUptimeResponse } from "lib/services/validator"; -import type { ComputedUptime, Ratio } from "lib/types"; +import type { ComputedUptime, Ratio, ValidatorAddr } from "lib/types"; import { formatRatio } from "lib/utils"; import { PenaltyEvent } from "./PenaltyEvent"; @@ -23,6 +23,7 @@ import { RecentBlocksLegends } from "./RecentBlocksLegends"; import { RecentBlocksSection } from "./RecentBlocksSection"; interface UptimeSectionProps { + validatorAddress: ValidatorAddr; uptimeData: ValidatorUptimeResponse; uptimeBlock: number; setUptimeBlock?: (block: number) => void; @@ -30,6 +31,7 @@ interface UptimeSectionProps { } export const UptimeSection = ({ + validatorAddress, uptimeData, uptimeBlock, setUptimeBlock, @@ -109,7 +111,9 @@ export const UptimeSection = ({ {onViewMore && ( <> - {isMobile && } + {isMobile && ( + + )} {uptimeData.events.length !== 0 && } {uptimeData.events.map((event) => ( diff --git a/src/lib/pages/validator-details/components/performance/index.tsx b/src/lib/pages/validator-details/components/performance/index.tsx index 9cb27c752..25b6a2830 100644 --- a/src/lib/pages/validator-details/components/performance/index.tsx +++ b/src/lib/pages/validator-details/components/performance/index.tsx @@ -34,6 +34,7 @@ export const Performance = ({ if (onViewMore) return ( setUptimeBlock(block)} @@ -60,7 +62,7 @@ export const Performance = ({ rounded={8} w="100%" > - + diff --git a/src/lib/pages/validator-details/components/validator-overview/index.tsx b/src/lib/pages/validator-details/components/validator-overview/index.tsx index 734dfdfbc..55e7b2690 100644 --- a/src/lib/pages/validator-details/components/validator-overview/index.tsx +++ b/src/lib/pages/validator-details/components/validator-overview/index.tsx @@ -57,7 +57,7 @@ export const ValidatorOverview = ({ ) : ( <> - + getValidatorUptime(endpoint, validatorAddress, blocks), { retry: 1, + refetchOnWindowFocus: false, } ); }; From aea43ad0e34484d287356f71f59d2d4e42d8c14d Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Mar 2024 12:34:45 +0700 Subject: [PATCH 2/6] docs: add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 784f4c5ec..2c49761de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#836](https://github.com/alleslabs/celatone-frontend/pull/836) Add recent 100 blocks chart - [#835](https://github.com/alleslabs/celatone-frontend/pull/835) Add amp view in json - block and proposal details - [#823](https://github.com/alleslabs/celatone-frontend/pull/823) Add validator uptime and penalty events - [#822](https://github.com/alleslabs/celatone-frontend/pull/822) Add validator list page From 2d2c5423378b9f8b3fc371a6d26097a2e50daa51 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Mar 2024 16:04:09 +0700 Subject: [PATCH 3/6] fix: use ref --- .../components/performance/PenaltySection.tsx | 4 +- .../performance/RecentBlocksSection.tsx | 96 ++++++++++--------- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/src/lib/pages/validator-details/components/performance/PenaltySection.tsx b/src/lib/pages/validator-details/components/performance/PenaltySection.tsx index 766487309..3f229b1dc 100644 --- a/src/lib/pages/validator-details/components/performance/PenaltySection.tsx +++ b/src/lib/pages/validator-details/components/performance/PenaltySection.tsx @@ -1,5 +1,6 @@ import { Flex, Heading, Text } from "@chakra-ui/react"; +import { CustomIcon } from "lib/components/icon"; import type { ValidatorUptimeResponse } from "lib/services/validator"; import { PenaltyEvent } from "./PenaltyEvent"; @@ -25,7 +26,8 @@ export const PenaltySection = ({ penaltyEvents }: PenaltySectionProps) => ( Latest 90 Days - + + {penaltyEvents.length === 0 ? ( This validator never had any slash or jailed history within 90 days. diff --git a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx index 677b090db..eb53622ed 100644 --- a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx +++ b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx @@ -1,6 +1,8 @@ import { Box, Flex, Grid, Heading, Text } from "@chakra-ui/react"; -import { useEffect, useRef } from "react"; +import { useRouter } from "next/router"; +import { forwardRef, useEffect, useRef } from "react"; +import { useNavContext } from "lib/app-provider"; import { Loading } from "lib/components/Loading"; import { Tooltip } from "lib/components/Tooltip"; import { useValidatorUptime } from "lib/services/validatorService"; @@ -11,52 +13,53 @@ import { formatUTC } from "lib/utils"; interface BlockProps { height: number; vote: BlockVote; - withCursor?: boolean; } -const Block = ({ height, vote, withCursor = false }: BlockProps) => { - let backgroundColor = "primary.main"; +const Block = forwardRef( + ({ height, vote }, ref) => { + let backgroundColor = "primary.main"; - if (vote === BlockVote.PROPOSE) { - backgroundColor = "secondary.main"; - } else if (vote === BlockVote.ABSTAIN) { - backgroundColor = "error.dark"; - } + if (vote === BlockVote.PROPOSE) { + backgroundColor = "secondary.main"; + } else if (vote === BlockVote.ABSTAIN) { + backgroundColor = "error.dark"; + } - let voteLabel = "Signed"; + let voteLabel = "Signed"; - if (vote === BlockVote.PROPOSE) { - voteLabel = "Proposed"; - } else if (vote === BlockVote.ABSTAIN) { - voteLabel = "Missed"; - } + if (vote === BlockVote.PROPOSE) { + voteLabel = "Proposed"; + } else if (vote === BlockVote.ABSTAIN) { + voteLabel = "Missed"; + } - return ( - - - - {withCursor && ( + return ( + + - )} - - - ); -}; + {ref && ( + + )} + + + ); + } +); interface RecentBlocksSectionProps { validatorAddress: ValidatorAddr; @@ -69,16 +72,17 @@ export const RecentBlocksSection = ({ validatorAddress, 100 ); + const { isExpand } = useNavContext(); + const router = useRouter(); const parentRef = useRef(null); + const cursorRef = useRef(null); const hoverTextRef = useRef(null); useEffect(() => { const handleResize = () => { const parentElement = parentRef.current; - const cursorElement = document.getElementById( - "most-recent-100-blocks-cursor" - ); + const cursorElement = cursorRef.current; const hoverTextElement = hoverTextRef.current; if (parentElement && cursorElement && hoverTextElement) { @@ -87,10 +91,14 @@ export const RecentBlocksSection = ({ const diffLeft = cursorRect.left - parentRect.left; const diffRight = parentRect.right - cursorRect.right; + if (diffLeft < 0 || diffRight < 0) return; + if (diffLeft < parentRect.width / 2) { hoverTextElement.style.left = `${diffLeft}px`; + hoverTextElement.style.right = "auto"; } else { hoverTextElement.style.right = `${diffRight}px`; + hoverTextElement.style.left = "auto"; } } }; @@ -101,7 +109,7 @@ export const RecentBlocksSection = ({ handleResize(); return () => window.removeEventListener("resize", handleResize); - }, [dataUpdatedAt]); + }, [dataUpdatedAt, isExpand, router.asPath]); if (isLoading) return ; @@ -136,7 +144,7 @@ export const RecentBlocksSection = ({ .map((block, index) => ( )) From 9d6163dbddd007db191befccb8a0a99262bca084 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Thu, 21 Mar 2024 16:18:35 +0700 Subject: [PATCH 4/6] fix: margin --- .../components/tables/ProposedBlocksTable.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/pages/validator-details/components/tables/ProposedBlocksTable.tsx b/src/lib/pages/validator-details/components/tables/ProposedBlocksTable.tsx index de6cccb95..58b9caf40 100644 --- a/src/lib/pages/validator-details/components/tables/ProposedBlocksTable.tsx +++ b/src/lib/pages/validator-details/components/tables/ProposedBlocksTable.tsx @@ -70,7 +70,8 @@ export const ProposedBlocksTable = ({ {isMobile ? ( From a3cacfd3828b3c0e7dce060d46ab96ce31df4d56 Mon Sep 17 00:00:00 2001 From: evilpeach Date: Fri, 22 Mar 2024 11:59:05 +0700 Subject: [PATCH 5/6] fix: as comments --- .../performance/RecentBlocksSection.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx index eb53622ed..2a9e98915 100644 --- a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx +++ b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx @@ -4,6 +4,7 @@ import { forwardRef, useEffect, useRef } from "react"; import { useNavContext } from "lib/app-provider"; import { Loading } from "lib/components/Loading"; +import { ErrorFetching } from "lib/components/state"; import { Tooltip } from "lib/components/Tooltip"; import { useValidatorUptime } from "lib/services/validatorService"; import { BlockVote } from "lib/types"; @@ -18,18 +19,13 @@ interface BlockProps { const Block = forwardRef( ({ height, vote }, ref) => { let backgroundColor = "primary.main"; - - if (vote === BlockVote.PROPOSE) { - backgroundColor = "secondary.main"; - } else if (vote === BlockVote.ABSTAIN) { - backgroundColor = "error.dark"; - } - let voteLabel = "Signed"; if (vote === BlockVote.PROPOSE) { + backgroundColor = "secondary.main"; voteLabel = "Proposed"; } else if (vote === BlockVote.ABSTAIN) { + backgroundColor = "error.dark"; voteLabel = "Missed"; } @@ -112,6 +108,7 @@ export const RecentBlocksSection = ({ }, [dataUpdatedAt, isExpand, router.asPath]); if (isLoading) return ; + if (!data) return ; return ( - {data?.recent100Blocks + {data.recent100Blocks .map((block, index) => ( - Most Recent Blocks: {data?.recent100Blocks[0].height ?? "N/A"} + Most Recent Blocks: {data.recent100Blocks[0].height ?? "N/A"} ); From e4c6683484f5de65035df69aca99462c5889065d Mon Sep 17 00:00:00 2001 From: evilpeach Date: Fri, 22 Mar 2024 12:11:41 +0700 Subject: [PATCH 6/6] fix: access array invalid index --- .../components/performance/RecentBlocksSection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx index 2a9e98915..3b2660862 100644 --- a/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx +++ b/src/lib/pages/validator-details/components/performance/RecentBlocksSection.tsx @@ -154,7 +154,7 @@ export const RecentBlocksSection = ({ bottom="0" ref={hoverTextRef} > - Most Recent Blocks: {data.recent100Blocks[0].height ?? "N/A"} + Most Recent Blocks: {data.recent100Blocks[0]?.height ?? "N/A"} );