diff --git a/explorer/src/App.tsx b/explorer/src/App.tsx index be665a6be6..238d98ba3b 100644 --- a/explorer/src/App.tsx +++ b/explorer/src/App.tsx @@ -12,6 +12,7 @@ import { ClusterStatsPage } from "pages/ClusterStatsPage"; import { SupplyPage } from "pages/SupplyPage"; import { TransactionDetailsPage } from "pages/TransactionDetailsPage"; import { BlockDetailsPage } from "pages/BlockDetailsPage"; +import { UnlockAlert } from "components/UnlockAlert"; const ADDRESS_ALIASES = ["account", "accounts", "addresses"]; const TX_ALIASES = ["txs", "txn", "txns", "transaction", "transactions"]; @@ -20,6 +21,7 @@ function App() { return ( <> +
diff --git a/explorer/src/components/UnlockAlert.tsx b/explorer/src/components/UnlockAlert.tsx new file mode 100644 index 0000000000..7099c5cd10 --- /dev/null +++ b/explorer/src/components/UnlockAlert.tsx @@ -0,0 +1,93 @@ +import React from "react"; + +import { Connection } from "@solana/web3.js"; +import { useCluster, Cluster } from "providers/cluster"; + +const CLUSTER_SYNC_INTERVAL = 30000; + +export function displayTimestamp(unixTimestamp: number): string { + const expireDate = new Date(unixTimestamp); + const dateString = new Intl.DateTimeFormat("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }).format(expireDate); + const timeString = new Intl.DateTimeFormat("en-US", { + hour: "numeric", + minute: "numeric", + hour12: false, + timeZoneName: "long", + }).format(expireDate); + return `${dateString} at ${timeString}`; +} + +export function UnlockAlert() { + const { cluster, url } = useCluster(); + const [active, setActive] = React.useState(false); + const [blockTime, setBlockTime] = React.useState(null); + + React.useEffect(() => { + if (!active || !url) { + return; + } + + const connection = new Connection(url); + const getBlockTime = async () => { + try { + const epochInfo = await connection.getEpochInfo(); + const blockTime = await connection.getBlockTime(epochInfo.absoluteSlot); + if (blockTime !== null) { + setBlockTime(blockTime); + } + } catch (error) {} + }; + + getBlockTime(); + + const blockTimeInterval = setInterval(getBlockTime, CLUSTER_SYNC_INTERVAL); + const secondInterval = setInterval(() => { + setBlockTime((time) => (time !== null ? time + 1 : null)); + }, 1000); + + return () => { + clearInterval(blockTimeInterval); + clearInterval(secondInterval); + }; + }, [active, url]); + + React.useEffect(() => { + if (cluster !== Cluster.MainnetBeta) { + return; + } + + setActive(true); + return () => { + setActive(false); + }; + }, [setActive, cluster]); + + if (cluster !== Cluster.MainnetBeta || blockTime === null) { + return null; + } + + return ( +
+

An unlock event is timed for midnight January 7th UTC cluster time.

+

+ Cluster time is currently {displayTimestamp(blockTime * 1000)} and may + differ from actual UTC time. +

+

+ More information can be found{" "} + + here + + . +

+
+ ); +} diff --git a/explorer/src/pages/ClusterStatsPage.tsx b/explorer/src/pages/ClusterStatsPage.tsx index b110afe2de..856667a662 100644 --- a/explorer/src/pages/ClusterStatsPage.tsx +++ b/explorer/src/pages/ClusterStatsPage.tsx @@ -88,7 +88,7 @@ function StatsCardBody() { )} {blockTime && ( - Block time + Cluster time {displayTimestamp(blockTime)}