From b5b0d37e94df0c810318265148202572e8835318 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Tue, 9 Sep 2025 09:29:56 +0800 Subject: [PATCH] added new page with node redistribution statistics --- src/components/Redistribution.tsx | 71 ++++++++++++++++++++++++++++++ src/components/SideBar.tsx | 6 +++ src/pages/info/index.tsx | 5 ++- src/pages/redistribution/index.tsx | 25 +++++++++++ src/providers/Bee.tsx | 18 ++++++++ src/routes.tsx | 3 ++ 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/components/Redistribution.tsx create mode 100644 src/pages/redistribution/index.tsx diff --git a/src/components/Redistribution.tsx b/src/components/Redistribution.tsx new file mode 100644 index 0000000..1e0c0c8 --- /dev/null +++ b/src/components/Redistribution.tsx @@ -0,0 +1,71 @@ +import { RedistributionState, BZZ, DAI } from '@ethersphere/bee-js' +import { useContext, useEffect, useState } from 'react' +import { Context } from '../providers/Settings' +import ExpandableListItem from './ExpandableListItem' + +export function Redistribution() { + const { beeApi } = useContext(Context) + const [redistributionState, setRedistributionState] = useState(null) + + useEffect(() => { + const interval = setInterval(() => { + if (!beeApi) { + return + } + + beeApi.getRedistributionState().then(setRedistributionState).catch(console.error) // eslint-disable-line + }, 3_000) + + return () => clearInterval(interval) + }) + + const formatDurationSeconds = (s?: number) => { + if (s === null || s === undefined) { + return '-' + } else { + return `${s} s` + } + } + + const formatBzzAmount = (amount?: BZZ) => { + if (amount === null || amount === undefined) { + return '-' + } else { + return `${amount.toSignificantDigits(4)} xBZZ` + } + } + + const formatDaiAmount = (amount?: DAI) => { + if (amount === null || amount === undefined) { + return '-' + } else { + return `${amount.toSignificantDigits(4)} xDAI` + } + } + + return ( + <> + + + + + + + + + + + + + + ) +} diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index b29d688..06011d8 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -7,6 +7,7 @@ import DocsIcon from 'remixicon-react/BookOpenLineIcon' import ExternalLinkIcon from 'remixicon-react/ExternalLinkLineIcon' import FileManagerIcon from 'remixicon-react/FolderOpenLineIcon' import GithubIcon from 'remixicon-react/GithubFillIcon' +import ExchangeDollarLineIcon from 'remixicon-react/ExchangeDollarLineIcon' import HomeIcon from 'remixicon-react/Home3LineIcon' import MenuFoldIcon from 'remixicon-react/MenuFoldLineIcon' import MenuUnfoldIcon from 'remixicon-react/MenuUnfoldLineIcon' @@ -147,6 +148,11 @@ export default function SideBar(): ReactElement { icon: AccountIcon, pathMatcherSubstring: '/account/', }, + { + label: 'Redistribution', + path: ROUTES.REDISTRIBUTION, + icon: ExchangeDollarLineIcon, + }, { label: 'Settings', path: ROUTES.SETTINGS, diff --git a/src/pages/info/index.tsx b/src/pages/info/index.tsx index 9f5d77f..f9d2a7e 100644 --- a/src/pages/info/index.tsx +++ b/src/pages/info/index.tsx @@ -15,7 +15,7 @@ import NodeInfoCard from './NodeInfoCard' import { WalletInfoCard } from './WalletInfoCard' export default function Status(): ReactElement { - const { beeVersion, status, topology, nodeInfo, walletBalance } = useContext(BeeContext) + const { beeVersion, status, topology, nodeInfo, nodeStatus, walletBalance } = useContext(BeeContext) const { isDesktop, desktopUrl } = useContext(SettingsContext) const { beeDesktopVersion } = useBeeDesktop(isDesktop, desktopUrl) const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isDesktop, desktopUrl, false) @@ -40,7 +40,10 @@ export default function Status(): ReactElement {
+ + +
diff --git a/src/pages/redistribution/index.tsx b/src/pages/redistribution/index.tsx new file mode 100644 index 0000000..dc8bb84 --- /dev/null +++ b/src/pages/redistribution/index.tsx @@ -0,0 +1,25 @@ +import CircularProgress from '@material-ui/core/CircularProgress' +import { ReactElement, useContext } from 'react' +import ExpandableList from '../../components/ExpandableList' +import { Redistribution } from '../../components/Redistribution' +import { Context as SettingsContext } from '../../providers/Settings' + +export default function RedistributionPage(): ReactElement { + const { isLoading } = useContext(SettingsContext) + + if (isLoading) { + return ( +
+ +
+ ) + } + + return ( + <> + + + + + ) +} diff --git a/src/providers/Bee.tsx b/src/providers/Bee.tsx index 9fbd5db..7b5bafe 100644 --- a/src/providers/Bee.tsx +++ b/src/providers/Bee.tsx @@ -5,11 +5,13 @@ import { ChainState, ChequebookAddressResponse, ChequebookBalanceResponse, + DebugStatus, LastChequesResponse, NodeAddresses, NodeInfo, Peer, PeerBalance, + RedistributionState, Topology, WalletBalance, } from '@ethersphere/bee-js' @@ -61,6 +63,7 @@ interface ContextInterface { apiHealth: boolean nodeAddresses: NodeAddresses | null nodeInfo: NodeInfo | null + nodeStatus: DebugStatus | null topology: Topology | null chequebookAddress: ChequebookAddressResponse | null peers: Peer[] | null @@ -71,6 +74,7 @@ interface ContextInterface { settlements: AllSettlements | null chainState: ChainState | null walletBalance: WalletBalance | null + redistributionState: RedistributionState | null latestBeeRelease: LatestBeeRelease | null isLoading: boolean lastUpdate: number | null @@ -91,6 +95,7 @@ const initialValues: ContextInterface = { apiHealth: false, nodeAddresses: null, nodeInfo: null, + nodeStatus: null, topology: null, chequebookAddress: null, stake: null, @@ -101,6 +106,7 @@ const initialValues: ContextInterface = { settlements: null, chainState: null, walletBalance: null, + redistributionState: null, latestBeeRelease: null, isLoading: true, lastUpdate: null, @@ -199,6 +205,7 @@ export function Provider({ children }: Props): ReactElement { const [isWarmingUp, setIsWarmingUp] = useState(true) const [nodeAddresses, setNodeAddresses] = useState(null) const [nodeInfo, setNodeInfo] = useState(null) + const [nodeStatus, setNodeStatus] = useState(null) const [topology, setNodeTopology] = useState(null) const [chequebookAddress, setChequebookAddress] = useState(null) const [peers, setPeers] = useState(null) @@ -209,6 +216,7 @@ export function Provider({ children }: Props): ReactElement { const [settlements, setSettlements] = useState(null) const [chainState, setChainState] = useState(null) const [walletBalance, setWalletBalance] = useState(null) + const [redistributionState, setRedistributionState] = useState(null) const [startedAt, setStartedAt] = useState(() => Date.now()) const { latestBeeRelease } = useLatestBeeRelease() @@ -257,6 +265,7 @@ export function Provider({ children }: Props): ReactElement { walletResult, chequebookBalanceResult, stakeResult, + redistributionStateResult, peerBalancesResult, settlementsResult, ] = await Promise.allSettled([ @@ -272,6 +281,7 @@ export function Provider({ children }: Props): ReactElement { beeApi.getWalletBalance({ timeout: TIMEOUT }), beeApi.getChequebookBalance({ timeout: TIMEOUT }), beeApi.getStake({ timeout: TIMEOUT }), + beeApi.getRedistributionState({ timeout: TIMEOUT }), beeApi.getAllBalances({ timeout: TIMEOUT }), beeApi.getAllSettlements(), ]) @@ -282,6 +292,7 @@ export function Provider({ children }: Props): ReactElement { setApiHealth(Boolean(health)) setIsWarmingUp(getFulfilledValue(statusResult)?.isWarmingUp ?? false) + setNodeStatus(getFulfilledValue(statusResult)) setNodeAddresses(getFulfilledValue(nodeAddressesResult)) setNodeInfo(getFulfilledValue(nodeInfoResult)) setNodeTopology(getFulfilledValue(topologyResult)) @@ -292,6 +303,7 @@ export function Provider({ children }: Props): ReactElement { setWalletBalance(getFulfilledValue(walletResult)) setChequebookBalance(getFulfilledValue(chequebookBalanceResult)) setStake(getFulfilledValue(stakeResult)) + setRedistributionState(getFulfilledValue(redistributionStateResult)) setPeerBalances(getFulfilledValue(peerBalancesResult)?.balances ?? null) setSettlements(getFulfilledValue(settlementsResult)) setError(null) @@ -332,6 +344,7 @@ export function Provider({ children }: Props): ReactElement { setNodeAddresses(null) setNodeTopology(null) setNodeInfo(null) + setNodeStatus(null) setPeers(null) setChequebookAddress(null) setChequebookBalance(null) @@ -339,6 +352,7 @@ export function Provider({ children }: Props): ReactElement { setPeerCheques(null) setSettlements(null) setChainState(null) + setRedistributionState(null) if (beeApi !== null) { refresh() @@ -381,6 +395,7 @@ export function Provider({ children }: Props): ReactElement { apiHealth, nodeAddresses, nodeInfo, + nodeStatus, topology, chequebookAddress, peers, @@ -391,6 +406,7 @@ export function Provider({ children }: Props): ReactElement { settlements, chainState, walletBalance, + redistributionState, latestBeeRelease, isLoading, lastUpdate, @@ -405,6 +421,7 @@ export function Provider({ children }: Props): ReactElement { apiHealth, nodeAddresses, nodeInfo, + nodeStatus, topology, chequebookAddress, peers, @@ -415,6 +432,7 @@ export function Provider({ children }: Props): ReactElement { settlements, chainState, walletBalance, + redistributionState, latestBeeRelease, isLoading, lastUpdate, diff --git a/src/routes.tsx b/src/routes.tsx index 3cc74f9..483c4af 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -28,6 +28,7 @@ import { CryptoTopUpIndex } from './pages/topUp/CryptoTopUpIndex' import { GiftCardFund } from './pages/topUp/GiftCardFund' import { GiftCardTopUpIndex } from './pages/topUp/GiftCardTopUpIndex' import { Swap } from './pages/topUp/Swap' +import RedistributionPage from './pages/redistribution' import { Context as SettingsContext } from './providers/Settings' export enum ROUTES { @@ -38,6 +39,7 @@ export enum ROUTES { UPLOAD_IN_PROGRESS = '/files/upload/workflow', DOWNLOAD = '/files/download', HASH = '/files/hash/:hash', + REDISTRIBUTION = '/redistribution', SETTINGS = '/settings', STATUS = '/status', TOP_UP = '/account/wallet/top-up', @@ -82,6 +84,7 @@ const BaseRouter = (): ReactElement => { } /> } /> } /> + } /> } /> } /> } />