feat(wallet-bonding): node settings wip
This commit is contained in:
@@ -36,7 +36,7 @@ export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; icon
|
||||
color: 'text.primary',
|
||||
}}
|
||||
>
|
||||
{!copied ? <ContentCopy fontSize="small" /> : <Check color="success" />}
|
||||
{!copied ? <ContentCopy sx={{ fontSize: 14 }} /> : <Check color="success" sx={{ fontSize: 14 }} />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MajorCurrencyAmount, TransactionExecuteResult } from '@nymproject/types';
|
||||
import { MajorCurrencyAmount, MixnodeStatus, TransactionExecuteResult } from '@nymproject/types';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import type { Network } from 'src/types';
|
||||
import { TBondGatewayArgs, TBondMixNodeArgs } from 'src/types';
|
||||
@@ -11,6 +11,26 @@ import {
|
||||
unbondMixNode as unbondMixNodeRequest,
|
||||
} from '../requests';
|
||||
|
||||
/* const bounded: BondedMixnode = {
|
||||
bond: { denom: 'NYM', amount: '1234' },
|
||||
key: 'B2Xx4haarLWMajX8w259oHjtRZsC7nHwagbWrJNiA3QC',
|
||||
delegators: 123,
|
||||
ip: '1.2.34.5',
|
||||
nodeRewards: { denom: 'NYM', amount: '123' },
|
||||
operatorRewards: { denom: 'NYM', amount: '12' },
|
||||
profitMargin: 10,
|
||||
stake: { denom: 'NYM', amount: '99' },
|
||||
stakeSaturation: 99,
|
||||
status: 'active',
|
||||
}; */
|
||||
|
||||
const bounded: BondedMixnode | BondedGateway = {
|
||||
bond: { denom: 'NYM', amount: '1234' },
|
||||
key: 'B2Xx4haarLWMajX8w259oHjtRZsC7nHwagbWrJNiA3QC',
|
||||
ip: '1.2.34.5',
|
||||
location: 'France',
|
||||
};
|
||||
|
||||
// TODO temporary type for bonded mixnode data
|
||||
export interface BondedMixnode {
|
||||
key: string;
|
||||
@@ -22,6 +42,7 @@ export interface BondedMixnode {
|
||||
nodeRewards: MajorCurrencyAmount;
|
||||
operatorRewards: MajorCurrencyAmount;
|
||||
delegators: number;
|
||||
status: MixnodeStatus;
|
||||
}
|
||||
|
||||
// TODO temporary type for bonded gateway data
|
||||
@@ -89,17 +110,6 @@ export const BondingContextProvider = ({
|
||||
};
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
const bounded: BondedMixnode = {
|
||||
bond: { denom: 'NYM', amount: '1234' },
|
||||
key: 'B2Xx4haarLWMajX8w259oHjtRZsC7nHwagbWrJNiA3QC',
|
||||
delegators: 123,
|
||||
ip: '1.2.34.5',
|
||||
nodeRewards: { denom: 'NYM', amount: '123' },
|
||||
operatorRewards: { denom: 'NYM', amount: '12' },
|
||||
profitMargin: 10,
|
||||
stake: { denom: 'NYM', amount: '99' },
|
||||
stakeSaturation: 99,
|
||||
};
|
||||
// TODO fetch bondedMixnode and bondedGatway via tauri dedicated requests
|
||||
/* try {
|
||||
bounded = await fetchBondingData();
|
||||
@@ -192,6 +202,7 @@ export const BondingContextProvider = ({
|
||||
error,
|
||||
bondMixnode,
|
||||
bondedMixnode,
|
||||
bondedGateway,
|
||||
bondGateway,
|
||||
unbondMixnode,
|
||||
unbondGateway,
|
||||
|
||||
@@ -16,6 +16,7 @@ const bondedMixnodeMock: BondedMixnode = {
|
||||
nodeRewards: { denom: 'NYM', amount: '1234' },
|
||||
operatorRewards: { denom: 'NYM', amount: '1234' },
|
||||
delegators: 5423,
|
||||
status: 'active',
|
||||
};
|
||||
|
||||
const bondedGatewayMock: BondedGateway = {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import * as React from 'react';
|
||||
import { Card, CardContent, CardHeader, Stack, Typography } from '@mui/material';
|
||||
import { MixnodeStatus } from '@nymproject/types';
|
||||
import { CheckCircleOutline, CircleOutlined, PauseCircleOutlined } from '@mui/icons-material';
|
||||
import { CopyToClipboard } from '../../components';
|
||||
import { splice } from '../../utils';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
title: string;
|
||||
identityKey: string;
|
||||
status?: MixnodeStatus;
|
||||
action?: React.ReactNode;
|
||||
}
|
||||
|
||||
const IdentityKey = ({ identityKey }: { identityKey: string }) => (
|
||||
<Typography variant="body2" component="span" fontWeight={400} sx={{ mr: 1, color: 'text.primary' }}>
|
||||
{splice(6, identityKey)}
|
||||
<CopyToClipboard text={identityKey} iconButton />
|
||||
</Typography>
|
||||
);
|
||||
|
||||
export const getNodeStatus = ({ status }: { status: MixnodeStatus }) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return (
|
||||
<Typography display="flex" alignItems="center" sx={{ color: 'success.main' }}>
|
||||
<CheckCircleOutline color="success" sx={{ mr: 0.5, fontSize: 13 }} /> Active
|
||||
</Typography>
|
||||
);
|
||||
case 'standby':
|
||||
return (
|
||||
<Typography display="flex" alignItems="center" sx={{ color: 'info.main' }}>
|
||||
<PauseCircleOutlined color="info" sx={{ mr: 0.5, fontSize: 13 }} /> Standby
|
||||
</Typography>
|
||||
);
|
||||
case 'inactive':
|
||||
return (
|
||||
<Typography display="flex" alignItems="center" sx={{ color: 'nym.text.dark' }}>
|
||||
<CircleOutlined sx={{ mr: 0.5, color: 'nym.text.dark', fontSize: 13 }} /> Inactive
|
||||
</Typography>
|
||||
);
|
||||
case 'not_found':
|
||||
return (
|
||||
<Typography display="flex" alignItems="center" sx={{ color: 'nym.text.dark' }}>
|
||||
<CircleOutlined sx={{ mr: 0.5, color: 'nym.text.dark', fontSize: 13 }} /> Not found
|
||||
</Typography>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const BondedNodeCard = (props: Props) => {
|
||||
const { title: rawTitle, identityKey, status: rawStatus, action, children } = props;
|
||||
let title: string | React.ReactNode = (
|
||||
<Typography fontSize={20} fontWeight={600}>
|
||||
{rawTitle}
|
||||
</Typography>
|
||||
);
|
||||
if (rawStatus) {
|
||||
title = (
|
||||
<Stack direction="row" spacing={1.2} alignItems="center">
|
||||
<Typography fontSize={20} fontWeight={600}>
|
||||
{rawTitle}
|
||||
</Typography>
|
||||
{getNodeStatus({ status: rawStatus })}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card variant="outlined" sx={{ overflow: 'auto', border: 'none', dropShadow: 'none' }}>
|
||||
<CardHeader
|
||||
title={title}
|
||||
subheader={<IdentityKey identityKey={identityKey} />}
|
||||
action={action}
|
||||
disableTypography
|
||||
sx={{ pb: 0 }}
|
||||
/>
|
||||
<CardContent sx={{ p: 3 }}>{children}</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default BondedNodeCard;
|
||||
@@ -1,11 +1,61 @@
|
||||
import React from 'react';
|
||||
import { Grid } from '@mui/material';
|
||||
import React, { useMemo } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import { MoreVert } from '@mui/icons-material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { NymCard } from '../../components';
|
||||
import { BondedGateway } from '../../context';
|
||||
import { Cell, Header, NodeTable } from './NodeTable';
|
||||
import BondedNodeCard from './BondedNodeCard';
|
||||
import { bondGateway } from '../../requests';
|
||||
|
||||
export const GatewayCard = () => (
|
||||
<NymCard title="Balance">
|
||||
<Grid container direction="column" spacing={2}>
|
||||
<Grid item>bonded gateway data table</Grid>
|
||||
</Grid>
|
||||
</NymCard>
|
||||
);
|
||||
const headers: Header[] = [
|
||||
{
|
||||
header: 'IP',
|
||||
id: 'ip-header',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
{
|
||||
header: 'Bond',
|
||||
id: 'bond-header',
|
||||
},
|
||||
{
|
||||
id: 'menu-button',
|
||||
size: 'small',
|
||||
sx: { pr: 0 },
|
||||
},
|
||||
];
|
||||
|
||||
export const GatewayCard = ({ gateway }: { gateway: BondedGateway }) => {
|
||||
const { ip, bond } = gateway;
|
||||
const theme = useTheme();
|
||||
const cells: Cell[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
cell: ip,
|
||||
id: 'ip-cell',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
{
|
||||
cell: `${bond.amount} ${bond.denom}`,
|
||||
id: 'bond-cell',
|
||||
},
|
||||
{
|
||||
cell: (
|
||||
<IconButton sx={{ fontSize: '1rem', padding: 0 }}>
|
||||
<MoreVert fontSize="inherit" sx={{ color: 'text.primary' }} />
|
||||
</IconButton>
|
||||
),
|
||||
id: 'menu-button-cell',
|
||||
align: 'center',
|
||||
size: 'small',
|
||||
sx: { pr: 0 },
|
||||
},
|
||||
],
|
||||
[gateway, theme],
|
||||
);
|
||||
return (
|
||||
<BondedNodeCard title="Valhalla gateway" identityKey={gateway.key}>
|
||||
<NodeTable headers={headers} cells={cells} />
|
||||
</BondedNodeCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { IconButton, Typography } from '@mui/material';
|
||||
import { Button, IconButton, Typography } from '@mui/material';
|
||||
import { MoreVert } from '@mui/icons-material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { NymCard } from '../../components';
|
||||
import { BondedMixnode } from '../../context';
|
||||
import { Node as NodeIcon } from '../../svg-icons/node';
|
||||
import { Cell, Header, NodeTable } from './NodeTable';
|
||||
import BondedNodeCard from './BondedNodeCard';
|
||||
|
||||
const headers: Header[] = [
|
||||
{
|
||||
@@ -51,6 +53,7 @@ const headers: Header[] = [
|
||||
|
||||
export const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
|
||||
const { stake, bond, stakeSaturation, profitMargin, nodeRewards, operatorRewards, delegators } = mixnode;
|
||||
const theme = useTheme();
|
||||
const cells: Cell[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -65,6 +68,7 @@ export const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
|
||||
{
|
||||
cell: `${stakeSaturation}%`,
|
||||
id: 'stake-saturation-cell',
|
||||
color: stakeSaturation > 100 ? theme.palette.nym.nymWallet.selectionChance.underModerate : undefined,
|
||||
},
|
||||
{
|
||||
cell: `${profitMargin}%`,
|
||||
@@ -94,10 +98,29 @@ export const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
|
||||
sx: { pr: 0 },
|
||||
},
|
||||
],
|
||||
[mixnode],
|
||||
[mixnode, theme],
|
||||
);
|
||||
return (
|
||||
<NymCard title="Monster node">
|
||||
<BondedNodeCard
|
||||
title="Monster node"
|
||||
identityKey={mixnode.key}
|
||||
status={mixnode.status}
|
||||
action={
|
||||
<Button
|
||||
variant="text"
|
||||
color="secondary"
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: 14,
|
||||
},
|
||||
}}
|
||||
startIcon={<NodeIcon />}
|
||||
>
|
||||
Node settings
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<NodeTable headers={headers} cells={cells} />
|
||||
<Typography sx={{ mt: 2 }}>
|
||||
Check more stats of your node on the{' '}
|
||||
@@ -105,6 +128,6 @@ export const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
|
||||
explorer
|
||||
</Link>
|
||||
</Typography>
|
||||
</NymCard>
|
||||
</BondedNodeCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface TableCell {
|
||||
export type TableHeader = TableCell & { tooltip?: React.ReactNode };
|
||||
|
||||
const CellHeader = ({ children, tooltip, sx, size, align, color }: TableHeader) => (
|
||||
<MUITableCell sx={{ py: 1.2, ...sx }} size={size} align={align} color={color}>
|
||||
<MUITableCell sx={{ py: 1.2, color, ...sx }} size={size} align={align}>
|
||||
{tooltip ? (
|
||||
<Tooltip title={tooltip} arrow placement="top-start">
|
||||
<Stack direction="row" alignItems="center" fontSize="0.8rem">
|
||||
@@ -40,7 +40,7 @@ const CellHeader = ({ children, tooltip, sx, size, align, color }: TableHeader)
|
||||
);
|
||||
|
||||
const CellValue = ({ children, align, size, color, sx }: TableCell) => (
|
||||
<MUITableCell component="th" scope="row" sx={{ py: 1, ...sx }} align={align} size={size} color={color}>
|
||||
<MUITableCell component="th" scope="row" sx={{ py: 1, color, ...sx }} align={align} size={size}>
|
||||
{children}
|
||||
</MUITableCell>
|
||||
);
|
||||
|
||||
@@ -27,6 +27,7 @@ const Bonding = () => {
|
||||
<PageLayout>
|
||||
<Box display="flex" flexDirection="column" gap={2}>
|
||||
{bondedMixnode && <MixnodeCard mixnode={bondedMixnode} />}
|
||||
{bondedGateway && <GatewayCard gateway={bondedGateway} />}
|
||||
</Box>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
Vendored
+11
@@ -64,6 +64,17 @@ declare module '@mui/material/styles' {
|
||||
nav: {
|
||||
background: string;
|
||||
};
|
||||
mixnodes: {
|
||||
status: {
|
||||
active: string;
|
||||
standby: string;
|
||||
};
|
||||
};
|
||||
selectionChance: {
|
||||
overModerate: string;
|
||||
moderate: string;
|
||||
underModerate: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,6 +56,17 @@ const darkMode: NymPaletteVariant = {
|
||||
nav: {
|
||||
background: '#292E34',
|
||||
},
|
||||
mixnodes: {
|
||||
status: {
|
||||
active: '#20D073',
|
||||
standby: '#5FD7EF',
|
||||
},
|
||||
},
|
||||
selectionChance: {
|
||||
overModerate: '#20D073',
|
||||
moderate: '#EBA53D',
|
||||
underModerate: '#DA465B',
|
||||
},
|
||||
};
|
||||
|
||||
const lightMode: NymPaletteVariant = {
|
||||
@@ -80,6 +91,17 @@ const lightMode: NymPaletteVariant = {
|
||||
nav: {
|
||||
background: '#FFFFFF',
|
||||
},
|
||||
mixnodes: {
|
||||
status: {
|
||||
active: '#1CBB67',
|
||||
standby: '#55C1D7',
|
||||
},
|
||||
},
|
||||
selectionChance: {
|
||||
overModerate: '#20D073',
|
||||
moderate: '#EBA53D',
|
||||
underModerate: '#DA465B',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user