diff --git a/package.json b/package.json index b33952e3..adf39047 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "lodash-es": "4.17.21", "mdast-util-from-markdown": "2.0.1", "micromark-extension-mdxjs": "3.0.0", + "moment": "^2.30.1", "next": "14.2.5", "next-redux-wrapper": "8.1.0", "next-seo": "6.0.0", @@ -134,4 +135,4 @@ } }, "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72" -} \ No newline at end of file +} diff --git a/src/components/Docs/components/GovUpgradeProposals.tsx b/src/components/Docs/components/GovUpgradeProposals.tsx index 2802fbf3..05400330 100644 --- a/src/components/Docs/components/GovUpgradeProposals.tsx +++ b/src/components/Docs/components/GovUpgradeProposals.tsx @@ -1,22 +1,19 @@ import { GitHub, InfoOutlined } from "@mui/icons-material"; import { Box, IconButton, Modal, Tooltip } from "@mui/material"; +import moment from "moment"; import { useEffect, useMemo, useState } from "react"; import { LoadingTable, NetworkTypeTabs, networkTypeTabs } from "~/components/shared"; import { NetworkType } from "~/lib/app.types"; -const API: Record = { +const API_PROPOSALS: Record = { testnet: "https://zetachain-athens.g.allthatnode.com/archive/rest/cosmos/gov/v1/proposals", mainnet: "https://zetachain-mainnet.g.allthatnode.com/archive/rest/cosmos/gov/v1/proposals", }; -const convertIpfsLink = (link: string) => { - const ipfsPrefix = "ipfs://"; - const gatewayPrefix = "https://ipfs.io/ipfs/"; - if (link.startsWith(ipfsPrefix)) { - return link.replace(ipfsPrefix, gatewayPrefix); - } - return link; +const API_BLOCKS: Record = { + testnet: "https://zetachain-athens.blockpi.network/lcd/v1/public/cosmos/base/tendermint/v1beta1/blocks/latest", + mainnet: "https://zetachain.blockpi.network/lcd/v1/public/cosmos/base/tendermint/v1beta1/blocks/latest", }; const modalStyles = { @@ -33,9 +30,19 @@ const modalStyles = { minWidth: "75%", }; +const convertIpfsLink = (link: string) => { + const ipfsPrefix = "ipfs://"; + const gatewayPrefix = "https://ipfs.io/ipfs/"; + if (link.startsWith(ipfsPrefix)) { + return link.replace(ipfsPrefix, gatewayPrefix); + } + return link; +}; + export const GovUpgradeProposals = () => { const [mainnetProposals, setMainnetProposals] = useState([]); const [testnetProposals, setTestnetProposals] = useState([]); + const [latestBlock, setLatestBlock] = useState(0); const [isLoading, setIsLoading] = useState(true); const [activeTab, setActiveTab] = useState(networkTypeTabs[0]); @@ -44,13 +51,32 @@ export const GovUpgradeProposals = () => { const [modalContents, setModalContents] = useState(undefined); const handleModalClose = () => setIsModalOpen(false); + useEffect(() => { + const fetchLatestBlock = async () => { + try { + const response = await fetch(API_BLOCKS[activeTab.networkType]); + const data = await response.json(); + setLatestBlock(parseInt(data.block.header.height, 10)); + } catch (error) { + console.error("Error fetching latest block:", error); + } + }; + + fetchLatestBlock(); + }, [activeTab.networkType]); + useEffect(() => { setIsLoading(true); - const fetchData = async () => { + const fetchProposals = async () => { try { - const response = await fetch(API[activeTab.networkType]); + const response = await fetch(API_PROPOSALS[activeTab.networkType]); const data = await response.json(); + if (!data || !data.proposals) { + console.error("Proposals data not found in the response"); + return; + } + const softwareUpgradeProposals = data.proposals .filter( (proposal: any) => @@ -69,19 +95,25 @@ export const GovUpgradeProposals = () => { if (activeTab.networkType === "mainnet") setMainnetProposals(softwareUpgradeProposals); if (activeTab.networkType === "testnet") setTestnetProposals(softwareUpgradeProposals); } catch (error) { - console.error("Error fetching data:", error); + console.error("Error fetching proposals:", error); } finally { setIsLoading(false); } }; - fetchData(); + fetchProposals(); }, [activeTab.networkType]); const proposals = useMemo(() => { return activeTab.networkType === "mainnet" ? mainnetProposals : testnetProposals; }, [activeTab.networkType, mainnetProposals, testnetProposals]); + const calculateUpgradeTime = (upgradeHeight: number) => { + const blocksRemaining = upgradeHeight - latestBlock; + const secondsRemaining = blocksRemaining * 6; + return moment().add(secondsRemaining, "seconds").fromNow(); + }; + return (
@@ -102,6 +134,7 @@ export const GovUpgradeProposals = () => { Upgrade Name Upgrade Height Status + Estimated Upgrade Time Details @@ -112,6 +145,7 @@ export const GovUpgradeProposals = () => { {proposal.plan.name} {proposal.plan.height} {proposal.status} + {calculateUpgradeTime(proposal.plan.height)} {proposal.plan.info.startsWith("{") ? ( <> diff --git a/yarn.lock b/yarn.lock index ce83a54c..1409f30f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11338,7 +11338,7 @@ mocha@^10.0.0: yargs-parser "^20.2.9" yargs-unparser "^2.0.0" -moment@^2.29.4: +moment@^2.29.4, moment@^2.30.1: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== @@ -13401,16 +13401,7 @@ streamx@^2.15.0, streamx@^2.20.0: optionalDependencies: bare-events "^2.2.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13517,7 +13508,7 @@ stringify-entities@^4.0.0: character-entities-html4 "^2.0.0" character-entities-legacy "^3.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -13531,13 +13522,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -14700,7 +14684,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -14718,15 +14702,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"