update ui and state
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import React from 'react'
|
||||
import { Typography } from '@mui/material'
|
||||
import { CircleOutlined, PauseCircleOutlined, CheckCircleOutline } from '@mui/icons-material'
|
||||
import { MixnodeStatus } from '../types'
|
||||
|
||||
export const NodeStatus = ({ status }: { status: MixnodeStatus }) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return <Active />
|
||||
case 'inactive':
|
||||
return <Inactive />
|
||||
case 'standby':
|
||||
return <Standby />
|
||||
default:
|
||||
null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const Active = () => (
|
||||
<Typography sx={{ color: 'success.main', display: 'flex', alignItems: 'center' }}>
|
||||
<CheckCircleOutline fontSize="small" color="success" sx={{ mr: 1 }} /> Active
|
||||
</Typography>
|
||||
)
|
||||
|
||||
const Inactive = () => (
|
||||
<Typography sx={{ color: 'nym.text.dark', display: 'flex', alignItems: 'center' }}>
|
||||
<CircleOutlined fontSize="small" sx={{ color: 'nym.text.dark', mr: 1 }} /> Inactive
|
||||
</Typography>
|
||||
)
|
||||
|
||||
const Standby = () => (
|
||||
<Typography sx={{ color: 'info.main', display: 'flex', alignItems: 'center' }}>
|
||||
<PauseCircleOutlined fontSize="small" color="info" sx={{ mr: 1 }} /> Standby
|
||||
</Typography>
|
||||
)
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { Card, CardContent, CardHeader } from '@mui/material'
|
||||
import { Box, Card, CardContent, CardHeader } from '@mui/material'
|
||||
import { styled } from '@mui/material/styles'
|
||||
|
||||
export const NymCard: React.FC<{
|
||||
@@ -16,7 +16,7 @@ export const NymCard: React.FC<{
|
||||
data-testid={title}
|
||||
titleTypographyProps={{ variant: 'h5' }}
|
||||
subheaderTypographyProps={{ variant: 'subtitle1' }}
|
||||
action={Action}
|
||||
action={<Box sx={{ mt: 1 }}>{Action}</Box>}
|
||||
sx={{
|
||||
color: 'nym.background.dark',
|
||||
py: 2.5,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SystemVariables } from './system-variables'
|
||||
import { NodeStats } from './node-stats'
|
||||
import { Overview } from './overview'
|
||||
import { useSettingsState } from './useSettingsState'
|
||||
import { NodeStatus } from '../../components/NodeStatus'
|
||||
|
||||
const tabs = ['Profile', 'System variables', 'Node stats']
|
||||
|
||||
@@ -16,26 +17,23 @@ export const Settings = () => {
|
||||
const { showSettings, handleShowSettings } = useContext(ClientContext)
|
||||
const [selectedTab, setSelectedTab] = useState(0)
|
||||
|
||||
const { mixnodeDetails, status, saturation } = useSettingsState(showSettings)
|
||||
const { mixnodeDetails, status, saturation, rewardEstimation } = useSettingsState(showSettings)
|
||||
|
||||
console.log({ status, saturation })
|
||||
|
||||
const handleTabChange = (event: React.SyntheticEvent, newTab: number) => setSelectedTab(newTab)
|
||||
const handleTabChange = (_: React.SyntheticEvent, newTab: number) => setSelectedTab(newTab)
|
||||
|
||||
return showSettings ? (
|
||||
<Dialog open={true} onClose={handleShowSettings} maxWidth="md" fullWidth>
|
||||
<NymCard
|
||||
title={
|
||||
<Box display="flex" alignItems="center">
|
||||
<SettingsOutlined sx={{ mr: 1 }} /> Settings
|
||||
<SettingsOutlined sx={{ mr: 1 }} />
|
||||
Node Settings
|
||||
</Box>
|
||||
}
|
||||
Action={<NodeStatus status={status} />}
|
||||
noPadding
|
||||
>
|
||||
<>
|
||||
<Typography variant="h5" sx={{ py: 2, px: 4 }}>
|
||||
Node settings
|
||||
</Typography>
|
||||
<Tabs tabs={tabs} selectedTab={selectedTab} onChange={handleTabChange} disabled={!mixnodeDetails} />
|
||||
<Overview details={mixnodeDetails} />
|
||||
{!mixnodeDetails && (
|
||||
@@ -44,7 +42,13 @@ export const Settings = () => {
|
||||
</Alert>
|
||||
)}
|
||||
{selectedTab === 0 && mixnodeDetails && <Profile />}
|
||||
{selectedTab === 1 && mixnodeDetails && <SystemVariables mixnodeDetails={mixnodeDetails.mix_node} />}
|
||||
{selectedTab === 1 && mixnodeDetails && (
|
||||
<SystemVariables
|
||||
mixnodeDetails={mixnodeDetails.mix_node}
|
||||
saturation={saturation}
|
||||
rewardEstimation={rewardEstimation}
|
||||
/>
|
||||
)}
|
||||
{selectedTab === 2 && mixnodeDetails && <NodeStats mixnodeId={mixnodeDetails.mix_node.identity_key} />}
|
||||
</>
|
||||
</NymCard>
|
||||
|
||||
@@ -1,40 +1,10 @@
|
||||
import React from 'react'
|
||||
import { Divider, Stack, Typography } from '@mui/material'
|
||||
import { CheckCircleOutline, CircleOutlined, PauseCircleOutlined } from '@mui/icons-material'
|
||||
import { TMixnodeBondDetails } from '../../types'
|
||||
|
||||
type TMixnodeStatus = 'active' | 'inactive' | 'standby'
|
||||
|
||||
export const Overview = ({
|
||||
mixnodeStatus,
|
||||
details,
|
||||
}: {
|
||||
mixnodeStatus?: TMixnodeStatus
|
||||
details?: TMixnodeBondDetails | null
|
||||
}) => (
|
||||
export const Overview = ({ details }: { details?: TMixnodeBondDetails | null }) => (
|
||||
<Stack spacing={3} sx={{ p: 4, pb: 0 }}>
|
||||
<Typography sx={{ color: 'grey.600' }}>Node identity {details?.mix_node.identity_key || 'n/a'}</Typography>
|
||||
{mixnodeStatus === 'active' && <ActiveMessage />}
|
||||
{mixnodeStatus === 'inactive' && <InActiveMessage />}
|
||||
{mixnodeStatus === 'standby' && <StandbyMessage />}
|
||||
<Divider />
|
||||
</Stack>
|
||||
)
|
||||
|
||||
const ActiveMessage = () => (
|
||||
<Typography sx={{ color: 'success.main', display: 'flex', alignItems: 'center' }}>
|
||||
<CheckCircleOutline fontSize="small" color="success" sx={{ mr: 1 }} /> Mixnode is active in this epoch
|
||||
</Typography>
|
||||
)
|
||||
|
||||
const InActiveMessage = () => (
|
||||
<Typography sx={{ color: 'nym.text.dark', display: 'flex', alignItems: 'center' }}>
|
||||
<CircleOutlined fontSize="small" sx={{ color: 'nym.text.dark', mr: 1 }} /> Mixnode is active in this epoch
|
||||
</Typography>
|
||||
)
|
||||
|
||||
const StandbyMessage = () => (
|
||||
<Typography sx={{ color: 'info.main', display: 'flex', alignItems: 'center' }}>
|
||||
<PauseCircleOutlined fontSize="small" color="info" sx={{ mr: 1 }} /> Mixnode is on standy by in this epoch
|
||||
</Typography>
|
||||
)
|
||||
|
||||
@@ -24,7 +24,15 @@ type TFormData = {
|
||||
profitMarginPercent: number
|
||||
}
|
||||
|
||||
export const SystemVariables = ({ mixnodeDetails }: { mixnodeDetails: TMixnodeBondDetails['mix_node'] }) => {
|
||||
export const SystemVariables = ({
|
||||
mixnodeDetails,
|
||||
saturation,
|
||||
rewardEstimation,
|
||||
}: {
|
||||
mixnodeDetails: TMixnodeBondDetails['mix_node']
|
||||
saturation: number
|
||||
rewardEstimation: number
|
||||
}) => {
|
||||
const [nodeUpdateResponse, setNodeUpdateResponse] = useState<'success' | 'failed'>()
|
||||
const [configFee, setConfigFee] = useState<string>()
|
||||
|
||||
@@ -78,7 +86,11 @@ export const SystemVariables = ({ mixnodeDetails }: { mixnodeDetails: TMixnodeBo
|
||||
<DataField
|
||||
title="Estimated reward"
|
||||
info="Estimated reward per epoch for this profit margin if your node is selected in the active set."
|
||||
Indicator={<Chip label="Coming soon" icon={<AccessTimeOutlined fontSize="small" />} />}
|
||||
Indicator={
|
||||
<Typography sx={{ color: (theme) => theme.palette.nym.fee, fontWeight: '600' }}>
|
||||
{rewardEstimation} {MAJOR_CURRENCY}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
<DataField
|
||||
@@ -96,7 +108,7 @@ export const SystemVariables = ({ mixnodeDetails }: { mixnodeDetails: TMixnodeBo
|
||||
<DataField
|
||||
title="Node stake saturation"
|
||||
info="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 1 million NYM, computed as S/K where S is the total amount of tokens available to stakeholders and K is the number of nodes in the reward set."
|
||||
Indicator={<Chip label="Coming soon" icon={<AccessTimeOutlined fontSize="small" />} />}
|
||||
Indicator={<PercentIndicator value={saturation} />}
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { ClientContext } from '../../context/main'
|
||||
import { getMixnodeBondDetails, getMixnodeStakeSaturation, getMixnodeStatus } from '../../requests'
|
||||
import { TMixnodeBondDetails, StakeSaturationResponse, MixnodeStatusResponse } from '../../types'
|
||||
import {
|
||||
getMixnodeBondDetails,
|
||||
getMixnodeRewardEstimation,
|
||||
getMixnodeStakeSaturation,
|
||||
getMixnodeStatus,
|
||||
minorToMajor,
|
||||
} from '../../requests'
|
||||
import { TMixnodeBondDetails, MixnodeStatus } from '../../types'
|
||||
|
||||
export const useSettingsState = (showSettings: boolean) => {
|
||||
const [mixnodeDetails, setMixnodeDetails] = useState<TMixnodeBondDetails | null>()
|
||||
const [status, setStatus] = useState<MixnodeStatusResponse>()
|
||||
const [saturation, setSaturation] = useState<StakeSaturationResponse>()
|
||||
const [status, setStatus] = useState<MixnodeStatus>('NotFound')
|
||||
const [saturation, setSaturation] = useState<number>(0)
|
||||
const [rewardEstimation, setRewardEstimation] = useState<number>(0)
|
||||
|
||||
const { clientDetails } = useContext(ClientContext)
|
||||
|
||||
@@ -18,14 +25,26 @@ export const useSettingsState = (showSettings: boolean) => {
|
||||
const getStatus = async () => {
|
||||
if (clientDetails?.client_address) {
|
||||
const status = await getMixnodeStatus(clientDetails?.contract_address)
|
||||
setStatus(status)
|
||||
setStatus(status.status)
|
||||
}
|
||||
}
|
||||
|
||||
const getStakeSaturation = async () => {
|
||||
if (clientDetails?.client_address) {
|
||||
const saturation = await getMixnodeStakeSaturation(clientDetails?.contract_address)
|
||||
setSaturation(saturation)
|
||||
if (saturation) {
|
||||
setSaturation(Math.round(saturation.saturation * 100))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getRewardEstimation = async () => {
|
||||
if (clientDetails?.client_address) {
|
||||
const rewardEstimation = await getMixnodeRewardEstimation(clientDetails?.contract_address)
|
||||
if (rewardEstimation) {
|
||||
const toMajor = await minorToMajor(rewardEstimation.estimated_total_node_reward.toString())
|
||||
setRewardEstimation(parseInt(toMajor.amount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +53,7 @@ export const useSettingsState = (showSettings: boolean) => {
|
||||
getBondDetails()
|
||||
getStatus()
|
||||
getStakeSaturation()
|
||||
getRewardEstimation()
|
||||
}
|
||||
}, [showSettings])
|
||||
|
||||
@@ -41,5 +61,6 @@ export const useSettingsState = (showSettings: boolean) => {
|
||||
status,
|
||||
saturation,
|
||||
mixnodeDetails,
|
||||
rewardEstimation,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
MixNode,
|
||||
MixnodeStatusResponse,
|
||||
Operation,
|
||||
RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
TauriContractStateParams,
|
||||
TauriTxResult,
|
||||
@@ -89,8 +90,11 @@ export const getMixnodeBondDetails = async (): Promise<TMixnodeBondDetails | nul
|
||||
export const getMixnodeStakeSaturation = async (identity: string): Promise<StakeSaturationResponse> =>
|
||||
await invoke('mixnode_stake_saturation', { identity })
|
||||
|
||||
export const getMixnodeRewardEstimation = async (identity: string): Promise<RewardEstimationResponse> =>
|
||||
await invoke('mixnode_reward_estimation', { identity })
|
||||
|
||||
export const getMixnodeStatus = async (identity: string): Promise<MixnodeStatusResponse> =>
|
||||
await invoke('mixnode_status', { identity })
|
||||
|
||||
|
||||
export const updateMixnode = async ({ profitMarginPercent }: { profitMarginPercent: number }) =>
|
||||
await invoke('update_mixnode', { profitMarginPercent })
|
||||
|
||||
@@ -12,3 +12,5 @@ export * from './transactiondetails'
|
||||
export * from './mixnodestatus'
|
||||
export * from './mixnodestatusresponse'
|
||||
export * from './stakesaturaionresponse'
|
||||
export * from './rewardestimationresponse'
|
||||
export * from './mixnodestatus'
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type MixnodeStatus = "Active" | "Standby" | "Inactive" | "NotFound";
|
||||
export type MixnodeStatus = 'active' | 'standby' | 'inactive' | 'NotFound'
|
||||
|
||||
Reference in New Issue
Block a user