feat: reviewed and simplified the node status check (#63)
* refactor: status page nested ternary logic * refactor: move the fetch latest bee release to a hook * refactor: solved node status rerendering, improved performance and clarity * refactor: step components now use unified hooks interface * style: removed component margins, layout should be handled by pages
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { ReactElement, useEffect, useState } 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 } from '@material-ui/icons/'
|
||||
import { CheckCircle, Error, Sync, ExpandLessSharp, ExpandMoreSharp, Autorenew } from '@material-ui/icons/'
|
||||
|
||||
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
|
||||
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
|
||||
@@ -9,17 +9,11 @@ import VersionCheck from './SetupSteps/VersionCheck'
|
||||
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
|
||||
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
|
||||
import PeerConnection from './SetupSteps/PeerConnection'
|
||||
import type {
|
||||
ChequebookAddressResponse,
|
||||
ChequebookBalanceResponse,
|
||||
Health,
|
||||
NodeAddresses,
|
||||
Topology,
|
||||
} from '@ethersphere/bee-js'
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
padding: theme.spacing(2),
|
||||
width: '100%',
|
||||
},
|
||||
button: {
|
||||
@@ -29,147 +23,88 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
actionsContainer: {
|
||||
margin: theme.spacing(2),
|
||||
},
|
||||
resetContainer: {
|
||||
padding: theme.spacing(5),
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
function getSteps() {
|
||||
return [
|
||||
'Debug Connection Check',
|
||||
'Version Check',
|
||||
'Connect to Ethereum Blockchain',
|
||||
'Deploy and Fund Chequebook',
|
||||
'Node Connection Check',
|
||||
'Connect to Peers',
|
||||
]
|
||||
interface Step {
|
||||
label: string
|
||||
condition: boolean
|
||||
isLoading: boolean
|
||||
component: ReactElement
|
||||
}
|
||||
|
||||
interface Props {
|
||||
nodeHealth: Health | null
|
||||
nodeApiHealth: boolean
|
||||
nodeAddresses: NodeAddresses | null
|
||||
chequebookAddress: ChequebookAddressResponse | null
|
||||
chequebookBalance: ChequebookBalanceResponse | null
|
||||
beeRelease: LatestBeeRelease | null
|
||||
nodeTopology: Topology | null
|
||||
isLoadingBeeRelease: boolean
|
||||
isLoadingNodeHealth: boolean
|
||||
isLoadingNodeAddresses: boolean
|
||||
isLoadingNodeTopology: boolean
|
||||
isLoadingHealth: boolean
|
||||
isLoadingChequebookAddress: boolean
|
||||
isLoadingChequebookBalance: boolean
|
||||
setStatusChecksVisible: (value: boolean) => void
|
||||
apiHost: string
|
||||
debugApiHost: string
|
||||
nodeVersion: StatusNodeVersionHook
|
||||
ethereumConnection: StatusEthereumConnectionHook
|
||||
debugApiConnection: StatusHookCommon
|
||||
apiConnection: StatusHookCommon
|
||||
topology: StatusTopologyHook
|
||||
chequebook: StatusChequebookHook
|
||||
}
|
||||
|
||||
function getStepContent(step: number, props: Props) {
|
||||
const {
|
||||
nodeHealth,
|
||||
debugApiHost,
|
||||
beeRelease,
|
||||
isLoadingBeeRelease,
|
||||
nodeAddresses,
|
||||
isLoadingNodeAddresses,
|
||||
isLoadingChequebookBalance,
|
||||
chequebookAddress,
|
||||
chequebookBalance,
|
||||
isLoadingChequebookAddress,
|
||||
nodeApiHealth,
|
||||
apiHost,
|
||||
isLoadingNodeTopology,
|
||||
nodeTopology,
|
||||
} = props
|
||||
switch (step) {
|
||||
case 0:
|
||||
return <DebugConnectionCheck nodeHealth={nodeHealth} debugApiHost={debugApiHost} />
|
||||
case 1:
|
||||
return <VersionCheck nodeHealth={nodeHealth} beeRelease={beeRelease} isLoadingBeeRelease={isLoadingBeeRelease} />
|
||||
case 2:
|
||||
return <EthereumConnectionCheck nodeAddresses={nodeAddresses} isLoadingNodeAddresses={isLoadingNodeAddresses} />
|
||||
case 3:
|
||||
return (
|
||||
<ChequebookDeployFund
|
||||
chequebookAddress={chequebookAddress}
|
||||
chequebookBalance={chequebookBalance}
|
||||
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
||||
isLoadingChequebookBalance={isLoadingChequebookBalance}
|
||||
/>
|
||||
)
|
||||
case 4:
|
||||
return <NodeConnectionCheck nodeApiHealth={nodeApiHealth} apiHost={apiHost} />
|
||||
default:
|
||||
return <PeerConnection nodeTopology={nodeTopology} isLoadingNodeTopology={isLoadingNodeTopology} />
|
||||
}
|
||||
}
|
||||
|
||||
export default function NodeSetupWorkflow(props: Props): ReactElement {
|
||||
const {
|
||||
nodeHealth,
|
||||
nodeApiHealth,
|
||||
nodeAddresses,
|
||||
chequebookAddress,
|
||||
chequebookBalance,
|
||||
beeRelease,
|
||||
nodeTopology,
|
||||
setStatusChecksVisible,
|
||||
} = props
|
||||
export default function NodeSetupWorkflow({
|
||||
nodeVersion,
|
||||
ethereumConnection,
|
||||
debugApiConnection,
|
||||
apiConnection,
|
||||
topology,
|
||||
chequebook,
|
||||
}: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
const [completed, setCompleted] = useState<{ [k: number]: boolean }>({})
|
||||
const steps = getSteps()
|
||||
const [activeStep, setActiveStep] = useState(-1)
|
||||
|
||||
const steps: Step[] = [
|
||||
{
|
||||
label: 'Connected to Node DebugAPI',
|
||||
condition: debugApiConnection.isOk,
|
||||
isLoading: debugApiConnection.isLoading,
|
||||
component: <DebugConnectionCheck {...debugApiConnection} />,
|
||||
},
|
||||
{
|
||||
label: 'Running latest Bee version',
|
||||
condition: nodeVersion.isOk,
|
||||
isLoading: nodeVersion.isLoading,
|
||||
component: <VersionCheck {...nodeVersion} />,
|
||||
},
|
||||
{
|
||||
label: 'Connected to Ethereum Blockchain',
|
||||
condition: ethereumConnection.isOk,
|
||||
isLoading: ethereumConnection.isLoading,
|
||||
component: <EthereumConnectionCheck {...ethereumConnection} />,
|
||||
},
|
||||
{
|
||||
label: 'Deployed and Funded Chequebook',
|
||||
condition: chequebook.isOk,
|
||||
isLoading: chequebook.isLoading,
|
||||
component: <ChequebookDeployFund {...chequebook} />,
|
||||
},
|
||||
{
|
||||
label: 'Connected to Node API',
|
||||
condition: apiConnection.isOk,
|
||||
isLoading: apiConnection.isLoading,
|
||||
component: <NodeConnectionCheck {...apiConnection} />,
|
||||
},
|
||||
{
|
||||
label: 'Connected to Peers',
|
||||
condition: topology.isOk,
|
||||
isLoading: topology.isLoading,
|
||||
component: <PeerConnection {...topology} />,
|
||||
},
|
||||
]
|
||||
|
||||
useEffect(() => {
|
||||
const handleComplete = (index: number) => {
|
||||
const newCompleted = completed
|
||||
newCompleted[index] = true
|
||||
setCompleted(newCompleted)
|
||||
}
|
||||
// If the user already changed the active step we don't want to overwrite it
|
||||
if (activeStep > 0 && activeStep < steps.length) return
|
||||
|
||||
const evaluateNodeStatus = () => {
|
||||
if (nodeHealth?.status === 'ok') {
|
||||
handleComplete(0)
|
||||
setActiveStep(1)
|
||||
}
|
||||
// Select first step that is not OK
|
||||
for (let i = 0; i < steps.length; ++i) {
|
||||
if (!steps[i].condition) {
|
||||
setActiveStep(i)
|
||||
|
||||
if (beeRelease && beeRelease.name === `v${nodeHealth?.version?.split('-')[0]}`) {
|
||||
handleComplete(1)
|
||||
setActiveStep(2)
|
||||
}
|
||||
|
||||
if (nodeAddresses?.ethereum) {
|
||||
handleComplete(2)
|
||||
setActiveStep(3)
|
||||
}
|
||||
|
||||
if (chequebookAddress?.chequebookaddress && chequebookBalance && chequebookBalance.totalBalance > 0) {
|
||||
handleComplete(3)
|
||||
setActiveStep(4)
|
||||
}
|
||||
|
||||
if (nodeApiHealth) {
|
||||
handleComplete(4)
|
||||
setActiveStep(5)
|
||||
}
|
||||
|
||||
if (nodeTopology?.connected && nodeTopology?.connected > 0) {
|
||||
handleComplete(5)
|
||||
setActiveStep(6)
|
||||
return
|
||||
}
|
||||
}
|
||||
evaluateNodeStatus()
|
||||
}, [
|
||||
nodeHealth,
|
||||
nodeApiHealth,
|
||||
nodeAddresses,
|
||||
chequebookAddress,
|
||||
beeRelease,
|
||||
chequebookBalance,
|
||||
nodeTopology,
|
||||
completed,
|
||||
])
|
||||
}, [steps])
|
||||
|
||||
const handleNext = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
@@ -179,13 +114,9 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}
|
||||
|
||||
const handleSetupComplete = () => {
|
||||
setStatusChecksVisible(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
<Paper className={classes.root}>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
Node Setup
|
||||
<span style={{ marginLeft: '25px' }}>
|
||||
<Button variant="outlined" size="small" onClick={() => window.location.reload()}>
|
||||
@@ -195,20 +126,22 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
|
||||
</span>
|
||||
</Typography>
|
||||
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
|
||||
{steps.map((label, index) => (
|
||||
{steps.map(({ label, condition, component, isLoading }, index) => (
|
||||
<Step key={label}>
|
||||
<StepLabel
|
||||
onClick={() => setActiveStep(index === activeStep ? 6 : index)}
|
||||
disabled={isLoading}
|
||||
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
|
||||
StepIconComponent={() => {
|
||||
if (completed[index]) {
|
||||
return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
|
||||
} else {
|
||||
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
|
||||
}
|
||||
if (isLoading) return <Autorenew style={{ height: '25px', cursor: 'pointer' }} />
|
||||
|
||||
if (condition) return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
|
||||
|
||||
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
|
||||
}}
|
||||
>
|
||||
<StepButton
|
||||
onClick={() => setActiveStep(index === activeStep ? 6 : index)}
|
||||
disabled={isLoading}
|
||||
onClick={() => setActiveStep(index === activeStep ? steps.length : index)}
|
||||
style={{ justifyContent: 'space-between' }}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>
|
||||
@@ -220,14 +153,14 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
|
||||
</StepButton>
|
||||
</StepLabel>
|
||||
<StepContent>
|
||||
<Typography component="div">{getStepContent(index, props)}</Typography>
|
||||
<Typography component="div">{component}</Typography>
|
||||
<div className={classes.actionsContainer}>
|
||||
<div>
|
||||
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
|
||||
Back
|
||||
</Button>
|
||||
<Button variant="contained" color="primary" onClick={handleNext} className={classes.button}>
|
||||
Next
|
||||
{index < steps.length - 1 ? 'Next' : 'Finish'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,17 +168,6 @@ export default function NodeSetupWorkflow(props: Props): ReactElement {
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
{Object.values(completed).filter(value => value).length === 6 ? (
|
||||
<Paper square elevation={0} className={classes.resetContainer}>
|
||||
<Typography>Bee setup complete! Welcome to the swarm and the internet of decentralized storage</Typography>
|
||||
<Button onClick={handleBack} className={classes.button}>
|
||||
Back
|
||||
</Button>
|
||||
<Button onClick={handleSetupComplete} variant="contained" color="primary" className={classes.button}>
|
||||
Complete
|
||||
</Button>
|
||||
</Paper>
|
||||
) : null}
|
||||
</div>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user