diff --git a/src/pages/status/NodeSetupWorkflow.tsx b/src/pages/status/NodeSetupWorkflow.tsx
index c8420b9..5673969 100644
--- a/src/pages/status/NodeSetupWorkflow.tsx
+++ b/src/pages/status/NodeSetupWorkflow.tsx
@@ -1,4 +1,4 @@
-import { ReactElement, useEffect, useState } from 'react'
+import { ReactElement, useEffect, useState, useContext } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/'
import { CheckCircle, Error, Sync, ExpandLessSharp, ExpandMoreSharp, Autorenew } from '@material-ui/icons/'
@@ -9,7 +9,7 @@ import VersionCheck from './SetupSteps/VersionCheck'
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
import PeerConnection from './SetupSteps/PeerConnection'
-import { StatusChequebookHook } from '../../hooks/status'
+import { Context } from '../../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -30,66 +30,65 @@ const useStyles = makeStyles((theme: Theme) =>
interface Step {
label: string
isOk: boolean
- isLoading: boolean
component: ReactElement
}
-interface Props {
- nodeVersion: StatusNodeVersionHook
- ethereumConnection: StatusEthereumConnectionHook
- debugApiConnection: StatusHookCommon
- apiConnection: StatusHookCommon
- topology: StatusTopologyHook
- chequebook: StatusChequebookHook
-}
-
-export default function NodeSetupWorkflow({
- nodeVersion,
- ethereumConnection,
- debugApiConnection,
- apiConnection,
- topology,
- chequebook,
-}: Props): ReactElement {
+export default function NodeSetupWorkflow(): ReactElement {
const classes = useStyles()
const [activeStep, setActiveStep] = useState(-1)
+ const {
+ status,
+ isLoading,
+ latestUserVersion,
+ latestPublishedVersion,
+ isLatestBeeVersion,
+ latestBeeVersionUrl,
+ topology,
+ nodeAddresses,
+ chequebookAddress,
+ } = useContext(Context)
+
const steps: Step[] = [
{
label: 'Connected to Node DebugAPI',
- isOk: debugApiConnection.isOk,
- isLoading: debugApiConnection.isLoading,
- component:
,
+ isOk: status.debugApiConnection,
+ component:
,
},
{
label: 'Running latest Bee version',
- isOk: nodeVersion.isOk,
- isLoading: nodeVersion.isLoading,
- component:
,
+ isOk: status.version,
+ component: (
+
+ ),
},
{
- label: 'Connected to Ethereum Blockchain',
- isOk: ethereumConnection.isOk,
- isLoading: ethereumConnection.isLoading,
- component:
,
+ label: 'Connected to xDai Blockchain',
+ isOk: status.blockchainConnection,
+ component:
,
},
{
label: 'Deployed and Funded Chequebook',
- isOk: chequebook.isOk,
- isLoading: chequebook.isLoading,
- component:
,
+ isOk: status.chequebook,
+ component: (
+
+ ),
},
{
label: 'Connected to Node API',
- isOk: apiConnection.isOk,
- isLoading: apiConnection.isLoading,
- component:
,
+ isOk: status.apiConnection,
+ component:
,
},
{
label: 'Connected to Peers',
- isOk: topology.isOk,
- isLoading: topology.isLoading,
- component:
,
+ isOk: status.topology,
+ component:
,
},
]
@@ -98,7 +97,7 @@ export default function NodeSetupWorkflow({
if (activeStep >= 0 && activeStep < steps.length) return
// If any step is not fully loaded yet return
- if (!steps.every(step => !step.isLoading)) return
+ if (!isLoading) return
// Select first step that is not OK
// This is deliberately a for loop (and not forEach) so that we can terminate the useEffect from within the cycle
@@ -131,10 +130,9 @@ export default function NodeSetupWorkflow({
- {steps.map(({ label, isOk, component, isLoading }, index) => (
+ {steps.map(({ label, isOk, component }, index) => (
setActiveStep(index === activeStep ? steps.length : index)}
StepIconComponent={() => {
if (isLoading) return
diff --git a/src/pages/status/SetupSteps/ChequebookDeployFund.tsx b/src/pages/status/SetupSteps/ChequebookDeployFund.tsx
index 456fa1e..5197f4f 100644
--- a/src/pages/status/SetupSteps/ChequebookDeployFund.tsx
+++ b/src/pages/status/SetupSteps/ChequebookDeployFund.tsx
@@ -2,22 +2,17 @@ import { Typography } from '@material-ui/core/'
import EthereumAddress from '../../../components/EthereumAddress'
import DepositModal from '../../../containers/DepositModal'
import type { ReactElement } from 'react'
-import type { StatusChequebookHook } from '../../../hooks/status'
-interface Props extends StatusChequebookHook {
- ethereumAddress?: string
+interface Props extends StatusHookCommon {
+ chequebookAddress?: string
}
-const ChequebookDeployFund = ({ isLoading, chequebookAddress, chequebookBalance }: Props): ReactElement | null => {
- if (isLoading) return null
-
+const ChequebookDeployFund = ({ chequebookAddress, isOk }: Props): ReactElement | null => {
return (
-
- {chequebookAddress?.chequebookAddress && }
-
+
{chequebookAddress && }
- {!(chequebookAddress?.chequebookAddress && chequebookBalance?.totalBalance.toBigNumber.isGreaterThan(0)) && (
+ {!isOk && (
Your chequebook is either not deployed or funded. To run the node you will need xDAI and xBZZ on the xDai
@@ -33,7 +28,7 @@ const ChequebookDeployFund = ({ isLoading, chequebookAddress, chequebookBalance
Chequebook Address
-
+
)
}
diff --git a/src/pages/status/SetupSteps/DebugConnectionCheck.tsx b/src/pages/status/SetupSteps/DebugConnectionCheck.tsx
index d61d646..94a512b 100644
--- a/src/pages/status/SetupSteps/DebugConnectionCheck.tsx
+++ b/src/pages/status/SetupSteps/DebugConnectionCheck.tsx
@@ -9,9 +9,7 @@ import { debugApiHost } from '../../../constants'
type Props = StatusHookCommon
-export default function NodeConnectionCheck({ isLoading, isOk }: Props): ReactElement | null {
- if (isLoading) return null
-
+export default function NodeConnectionCheck({ isOk }: Props): ReactElement | null {
const changeDebugApiUrl = (
diff --git a/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx b/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx
index dce48f1..2c72ca2 100644
--- a/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx
+++ b/src/pages/status/SetupSteps/EthereumConnectionCheck.tsx
@@ -4,9 +4,7 @@ import EthereumAddress from '../../../components/EthereumAddress'
type Props = StatusEthereumConnectionHook
-export default function EthereumConnectionCheck({ isLoading, isOk, nodeAddresses }: Props): ReactElement | null {
- if (isLoading) return null
-
+export default function EthereumConnectionCheck({ isOk, nodeAddresses }: Props): ReactElement | null {
if (isOk) {
return (
diff --git a/src/pages/status/SetupSteps/NodeConnectionCheck.tsx b/src/pages/status/SetupSteps/NodeConnectionCheck.tsx
index f571451..6078937 100644
--- a/src/pages/status/SetupSteps/NodeConnectionCheck.tsx
+++ b/src/pages/status/SetupSteps/NodeConnectionCheck.tsx
@@ -8,9 +8,7 @@ import { apiHost } from '../../../constants'
type Props = StatusHookCommon
-export default function NodeConnectionCheck({ isLoading, isOk }: Props): ReactElement | null {
- if (isLoading) return null
-
+export default function NodeConnectionCheck({ isOk }: Props): ReactElement | null {
return (
diff --git a/src/pages/status/SetupSteps/PeerConnection.tsx b/src/pages/status/SetupSteps/PeerConnection.tsx
index bb4cd6b..72c21a2 100644
--- a/src/pages/status/SetupSteps/PeerConnection.tsx
+++ b/src/pages/status/SetupSteps/PeerConnection.tsx
@@ -3,9 +3,7 @@ import { Typography } from '@material-ui/core/'
type Props = StatusTopologyHook
-export default function PeerConnection({ isLoading, isOk, topology }: Props): ReactElement | null {
- if (isLoading) return null
-
+export default function PeerConnection({ isOk, topology }: Props): ReactElement | null {
const peers = (
diff --git a/src/pages/status/SetupSteps/VersionCheck.tsx b/src/pages/status/SetupSteps/VersionCheck.tsx
index 89ac0fd..01bbd62 100644
--- a/src/pages/status/SetupSteps/VersionCheck.tsx
+++ b/src/pages/status/SetupSteps/VersionCheck.tsx
@@ -4,15 +4,7 @@ import CodeBlockTabs from '../../../components/CodeBlockTabs'
type Props = StatusNodeVersionHook
-export default function VersionCheck({
- isLoading,
- isOk,
- userVersion,
- latestVersion,
- latestUrl,
-}: Props): ReactElement | null {
- if (isLoading) return null
-
+export default function VersionCheck({ isOk, userVersion, latestVersion, latestUrl }: Props): ReactElement | null {
const version = (
diff --git a/src/pages/status/index.tsx b/src/pages/status/index.tsx
index 8a21eaf..5d4ed16 100644
--- a/src/pages/status/index.tsx
+++ b/src/pages/status/index.tsx
@@ -1,18 +1,10 @@
-import { ReactElement } from 'react'
-import { Container, CircularProgress } from '@material-ui/core'
+import { ReactElement, useContext } from 'react'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import NodeSetupWorkflow from './NodeSetupWorkflow'
import StatusCard from './StatusCard'
import EthereumAddressCard from '../../components/EthereumAddressCard'
-import {
- useStatusEthereumConnection,
- useStatusNodeVersion,
- useStatusDebugConnection,
- useStatusConnection,
- useStatusTopology,
- useStatusChequebook,
-} from '../../hooks/status'
+import { Context } from '../../providers/Bee'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -27,50 +19,30 @@ const useStyles = makeStyles((theme: Theme) =>
export default function Status(): ReactElement {
const classes = useStyles()
- const nodeVersion = useStatusNodeVersion()
- const ethereumConnection = useStatusEthereumConnection()
- const debugApiConnection = useStatusDebugConnection()
- const apiConnection = useStatusConnection()
- const topology = useStatusTopology()
- const chequebook = useStatusChequebook()
-
- const checks = [nodeVersion, ethereumConnection, debugApiConnection, apiConnection, topology, chequebook]
-
- // If any check data are still loading
- if (!checks.every(c => !c.isLoading)) {
- return (
-
-
-
- )
- }
+ const {
+ status,
+ latestUserVersion,
+ isLatestBeeVersion,
+ latestBeeVersionUrl,
+ topology,
+ nodeAddresses,
+ chequebookAddress,
+ } = useContext(Context)
return (
c.isOk)}
- nodeTopology={topology.topology}
- latestUrl={nodeVersion.latestUrl}
- nodeAddresses={ethereumConnection.nodeAddresses}
+ userBeeVersion={latestUserVersion}
+ isLatestBeeVersion={isLatestBeeVersion}
+ isOk={status.all}
+ nodeTopology={topology}
+ latestUrl={latestBeeVersionUrl}
+ nodeAddresses={nodeAddresses}
/>
- {ethereumConnection.nodeAddresses && chequebook.chequebookAddress && (
-
+ {nodeAddresses && chequebookAddress && (
+
)}
-
+
)
}
diff --git a/src/providers/Bee.tsx b/src/providers/Bee.tsx
new file mode 100644
index 0000000..89ff3a5
--- /dev/null
+++ b/src/providers/Bee.tsx
@@ -0,0 +1,256 @@
+import type { ChequebookBalance, Balance, Settlements } from '../types'
+import { createContext, ReactChild, ReactElement, useEffect, useState } from 'react'
+import { beeApi, beeDebugApi } from '../services/bee'
+import { Token } from '../models/Token'
+import semver from 'semver'
+import { engines } from '../../package.json'
+
+import type {
+ NodeAddresses,
+ ChequebookAddressResponse,
+ LastChequesResponse,
+ Health,
+ Peer,
+ Topology,
+} from '@ethersphere/bee-js'
+import { useLatestBeeRelease } from '../hooks/apiHooks'
+
+interface Status {
+ all: boolean
+ version: boolean
+ blockchainConnection: boolean
+ debugApiConnection: boolean
+ apiConnection: boolean
+ topology: boolean
+ chequebook: boolean
+}
+
+interface ContextInterface {
+ status: Status
+ latestPublishedVersion?: string
+ latestUserVersion?: string
+ latestUserVersionExact?: string
+ isLatestBeeVersion: boolean
+ latestBeeVersionUrl: string
+ error: Error | null
+ apiHealth: boolean
+ debugApiHealth: Health | null
+ nodeAddresses: NodeAddresses | null
+ topology: Topology | null
+ chequebookAddress: ChequebookAddressResponse | null
+ peers: Peer[] | null
+ chequebookBalance: ChequebookBalance | null
+ peerBalances: Balance[] | null
+ peerCheques: LastChequesResponse | null
+ settlements: Settlements | null
+ latestBeeRelease: LatestBeeRelease | null
+ isLoading: boolean
+ isRefreshing: boolean
+ lastUpdate: number | null
+ start: (frequency?: number) => void
+ stop: () => void
+ refresh: () => Promise
+}
+
+const initialValues: ContextInterface = {
+ status: {
+ all: false,
+ version: false,
+ blockchainConnection: false,
+ debugApiConnection: false,
+ apiConnection: false,
+ topology: false,
+ chequebook: false,
+ },
+ latestPublishedVersion: undefined,
+ latestUserVersion: undefined,
+ latestUserVersionExact: undefined,
+ isLatestBeeVersion: false,
+ latestBeeVersionUrl: 'https://github.com/ethersphere/bee/releases/latest',
+ error: null,
+ apiHealth: false,
+ debugApiHealth: null,
+ nodeAddresses: null,
+ topology: null,
+ chequebookAddress: null,
+ peers: null,
+ chequebookBalance: null,
+ peerBalances: null,
+ peerCheques: null,
+ settlements: null,
+ latestBeeRelease: null,
+ isLoading: true,
+ isRefreshing: false,
+ lastUpdate: null,
+ start: () => {}, // eslint-disable-line
+ stop: () => {}, // eslint-disable-line
+ refresh: () => Promise.reject(),
+}
+
+export const Context = createContext(initialValues)
+export const Consumer = Context.Consumer
+
+interface Props {
+ children: ReactChild
+}
+
+function getStatus(
+ latestBeeRelease: LatestBeeRelease | null,
+ debugApiHealth: Health | null,
+ nodeAddresses: NodeAddresses | null,
+ apiHealth: boolean,
+ topology: Topology | null,
+ chequebookAddress: ChequebookAddressResponse | null,
+ chequebookBalance: ChequebookBalance | null,
+ error: Error | null,
+): Status {
+ const status = {
+ version: Boolean(
+ debugApiHealth &&
+ semver.satisfies(debugApiHealth.version, engines.bee, {
+ includePrerelease: true,
+ }),
+ ),
+ blockchainConnection: Boolean(nodeAddresses?.ethereum),
+ debugApiConnection: Boolean(debugApiHealth?.status === 'ok'),
+ apiConnection: apiHealth,
+ topology: Boolean(topology?.connected && topology?.connected > 0),
+ chequebook:
+ Boolean(chequebookAddress?.chequebookAddress) &&
+ chequebookBalance !== null &&
+ chequebookBalance?.totalBalance.toBigNumber.isGreaterThan(0),
+ }
+
+ return { ...status, all: !error && Object.values(status).every(v => v) }
+}
+
+export function Provider({ children }: Props): ReactElement {
+ const [apiHealth, setApiHealth] = useState(false)
+ const [debugApiHealth, setDebugApiHealth] = useState(null)
+ const [nodeAddresses, setNodeAddresses] = useState(null)
+ const [topology, setNodeTopology] = useState(null)
+ const [chequebookAddress, setChequebookAddress] = useState(null)
+ const [peers, setPeers] = useState(null)
+ const [chequebookBalance, setChequebookBalance] = useState(null)
+ const [peerBalances, setPeerBalances] = useState(null)
+ const [peerCheques, setPeerCheques] = useState(null)
+ const [settlements, setSettlements] = useState(null)
+ const { latestBeeRelease } = useLatestBeeRelease()
+
+ const [error, setError] = useState(initialValues.error)
+ const [isLoading, setIsLoading] = useState(initialValues.isLoading)
+ const [isRefreshing, setIsRefreshing] = useState(initialValues.isRefreshing)
+ const [lastUpdate, setLastUpdate] = useState(initialValues.lastUpdate)
+ const [frequency, setFrequency] = useState(30000)
+
+ const latestPublishedVersion = semver.coerce(latestBeeRelease?.name)?.version
+ const latestUserVersion = semver.coerce(debugApiHealth?.version)?.version
+ const latestUserVersionExact = debugApiHealth?.version
+
+ const refresh = async () => {
+ // Don't want to refresh when already refreshing
+ if (isRefreshing) return
+
+ try {
+ setIsRefreshing(true)
+
+ setApiHealth(await beeApi.status.health())
+ setDebugApiHealth(await beeDebugApi.status.nodeHealth())
+ setNodeAddresses(await beeDebugApi.connectivity.addresses())
+ setNodeTopology(await beeDebugApi.connectivity.topology())
+ setChequebookAddress(await beeDebugApi.chequebook.address())
+ setPeers(await beeDebugApi.connectivity.listPeers())
+
+ const { totalBalance, availableBalance } = await beeDebugApi.chequebook.balance()
+ setChequebookBalance({
+ totalBalance: new Token(totalBalance),
+ availableBalance: new Token(availableBalance),
+ })
+
+ const { balances } = await beeDebugApi.balance.balances()
+ setPeerBalances(balances.map(({ peer, balance }) => ({ peer, balance: new Token(balance) })))
+
+ setPeerCheques(await beeDebugApi.chequebook.getLastCheques())
+ const { totalReceived, settlements, totalSent } = await beeDebugApi.settlements.getSettlements()
+ setSettlements({
+ totalReceived: new Token(totalReceived),
+ totalSent: new Token(totalSent),
+ settlements: settlements.map(({ peer, received, sent }) => ({
+ peer,
+ received: new Token(received),
+ sent: new Token(sent),
+ })),
+ })
+
+ setLastUpdate(Date.now())
+ } catch (e) {
+ setError(e)
+ } finally {
+ setIsLoading(false)
+ setIsRefreshing(false)
+ }
+ }
+
+ const start = (freq = 30000) => setFrequency(freq)
+ const stop = () => setFrequency(null)
+
+ // Start the update loop
+ useEffect(() => {
+ refresh()
+
+ // Start autorefresh only if the frequency is set
+ if (frequency) {
+ const interval = setInterval(refresh, frequency)
+
+ return () => clearInterval(interval)
+ }
+ }, [frequency])
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
index acedcb3..246a950 100644
--- a/src/react-app-env.d.ts
+++ b/src/react-app-env.d.ts
@@ -6,7 +6,6 @@ interface LatestBeeRelease {
}
interface StatusHookCommon {
- isLoading: boolean
isOk: boolean
}
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..8fd9a7f
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,23 @@
+import type { Token } from './models/Token'
+
+export interface ChequebookBalance {
+ totalBalance: Token
+ availableBalance: Token
+}
+
+export interface Balance {
+ peer: string
+ balance: Token
+}
+
+export interface Settlement {
+ peer: string
+ received: Token
+ sent: Token
+}
+
+export interface Settlements {
+ totalReceived: Token
+ totalSent: Token
+ settlements: Settlement[]
+}