feat(wallet-bonding): node settings flow

This commit is contained in:
pierre
2022-06-28 17:30:09 +02:00
committed by fmtabbara
parent 8d3f1a3c38
commit 3382642d70
7 changed files with 171 additions and 58 deletions
@@ -177,6 +177,7 @@ const BondingCard = () => {
confirmButton="Done"
maxWidth="xs"
fullWidth
sx={{ textAlign: 'center' }}
>
<Link href={`${urls(network).blockExplorer}/transaction/${state.tx?.transaction_hash}`} noIcon>
View on blockchain
@@ -33,3 +33,9 @@ Default.args = {
cancelButton: false,
disabled: false,
};
export const CenteredText = Template.bind({});
CenteredText.args = {
...Default.args,
sx: { textAlign: 'center' },
};
@@ -130,7 +130,7 @@ const MixnodeCard = ({ mixnode }: { mixnode: BondedMixnode }) => {
explorer
</Link>
</Typography>
<NodeSettings mixnode={mixnode} open={showNodeSettings} />
<NodeSettings mixnode={mixnode} show={showNodeSettings} onClose={() => setShowNodeSettings(false)} />
</BondedNodeCard>
);
};
@@ -1,35 +1,82 @@
import * as React from 'react';
import { useState } from 'react';
import { MajorCurrencyAmount } from '@nymproject/types';
import { useContext, useEffect, useState } from 'react';
import { MajorCurrencyAmount, TransactionExecuteResult } from '@nymproject/types';
import { Link } from '@nymproject/react/link/Link';
import { Typography } from '@mui/material';
import ProfitMarginModal from './ProfitMarginModal';
import { BondedMixnode } from '../../../../context';
import { AppContext, BondedMixnode, urls } from '../../../../context';
import SummaryModal from './SummaryModal';
import { SimpleDialog } from '../../components';
interface Props {
mixnode: BondedMixnode;
open: boolean;
show: boolean;
onClose: () => void;
}
// TODO fetch real estimated operator reward for 10% PM
const MOCK_ESTIMATED_OP_REWARD: MajorCurrencyAmount = { amount: '42', denom: 'NYM' };
const NodeSettings = ({ open, mixnode }: Props) => {
const NodeSettings = ({ mixnode, show, onClose }: Props) => {
const [profitMargin, setProfitMargin] = useState<number>();
const [pmModalOpen, setPmModalOpen] = useState<boolean>(true);
const [fee, setFee] = useState<MajorCurrencyAmount>({ amount: '0', denom: 'NYM' });
const [step, setStep] = useState<1 | 2 | 3>(1);
const [tx, setTx] = useState<TransactionExecuteResult>();
if (!open) return null;
const { network } = useContext(AppContext);
useEffect(() => {
setFee({ amount: '42', denom: 'NYM' }); // TODO fetch real fee amount
}, [profitMargin]);
const submit = () => {
// TODO send request to update profit margin
setStep(3); // on success
// setTx(requestResult)
};
const reset = () => {
setProfitMargin(0);
setStep(1);
onClose();
};
return (
<ProfitMarginModal
open={pmModalOpen}
onClose={() => {
setPmModalOpen(false);
}}
onSubmit={async (pm) => {
setProfitMargin(pm);
setPmModalOpen(false);
}}
estimatedOpReward={MOCK_ESTIMATED_OP_REWARD}
currentPm={mixnode.profitMargin}
/>
<>
<ProfitMarginModal
open={show && step === 1}
onClose={onClose}
onConfirm={async (pm) => {
setProfitMargin(pm);
setStep(2);
}}
estimatedOpReward={MOCK_ESTIMATED_OP_REWARD}
currentPm={mixnode.profitMargin}
/>
<SummaryModal
open={show && step === 2}
onClose={reset}
onConfirm={submit}
onCancel={() => setStep(1)}
currentPm={mixnode.profitMargin}
newPm={profitMargin as number}
fee={fee as MajorCurrencyAmount}
/>
<SimpleDialog
open={show && step === 3}
onClose={reset}
onConfirm={reset}
title="Operation successful"
confirmButton="Done"
maxWidth="xs"
sx={{ textAlign: 'center' }}
>
<Typography sx={{ mb: 2 }}>This operation can take up to one hour to process</Typography>
<Link href={`${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`} noIcon>
View on blockchain
</Link>
</SimpleDialog>
</>
);
};
@@ -1,15 +1,16 @@
import * as React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { Divider, Stack, Typography } from '@mui/material';
import { Box, Divider, Stack, Typography } from '@mui/material';
import { MajorCurrencyAmount } from '@nymproject/types';
import { SimpleDialog, TextFieldInput } from '../../components';
import schema from './schema';
import { Node as NodeIcon } from '../../../../svg-icons/node';
import getSchema from './schema';
export interface Props {
open: boolean;
onClose: () => void;
onSubmit: (pm: number) => Promise<void>;
onConfirm: (pm: number) => void;
estimatedOpReward: MajorCurrencyAmount;
currentPm: number;
}
@@ -18,15 +19,14 @@ interface FormData {
profitMargin: number;
}
const NodeSettingsModal = ({ open, onClose, onSubmit, estimatedOpReward, currentPm }: Props) => {
const NodeSettingsModal = ({ open, onClose, onConfirm, estimatedOpReward, currentPm }: Props) => {
const {
control,
setValue,
setError,
handleSubmit,
reset,
formState: { errors },
} = useForm<FormData>({
resolver: yupResolver(schema),
resolver: yupResolver(getSchema(currentPm)),
defaultValues: {
profitMargin: currentPm,
},
@@ -35,37 +35,48 @@ const NodeSettingsModal = ({ open, onClose, onSubmit, estimatedOpReward, current
return (
<SimpleDialog
open={open}
onClose={onClose}
onConfirm={handleSubmit(async (data) => onSubmit(data.profitMargin))}
title="Node Settings"
onClose={() => {
reset();
onClose();
}}
onConfirm={handleSubmit(async (data) => onConfirm(data.profitMargin))}
title={
<Stack direction="row" alignItems="center">
<NodeIcon sx={{ mr: 1, fontSize: 14 }} />
Node Settings
</Stack>
}
subTitle="System Variables"
confirmButton="Next"
closeButton
disabled={Boolean(errors?.profitMargin)}
>
<form>
<TextFieldInput
name="profitMargin"
control={control}
defaultValue=""
label="Set profit margin"
placeholder="Profit Margin"
error={Boolean(errors.profitMargin)}
helperText={
errors.profitMargin
? errors.profitMargin.message
: 'Your new profit margin will be applied in the next epoch'
}
required
muiTextFieldProps={{ fullWidth: true }}
sx={{ mb: 2.5 }}
/>
</form>
<Stack direction="row" justifyContent="space-between" mt={3}>
<Typography fontWeight={400}>Estimated operator reward for 10% PM</Typography>
<Typography fontWeight={400}>{`~${estimatedOpReward.amount} ${estimatedOpReward.denom}`}</Typography>
</Stack>
<Divider sx={{ my: 1 }} />
<Typography fontWeight={400}>Est. fee for this transaction will be cauculated in the next page</Typography>
<Box sx={{ mt: 1 }}>
<form>
<TextFieldInput
name="profitMargin"
control={control}
defaultValue=""
label="Set profit margin"
placeholder="Profit Margin"
error={Boolean(errors.profitMargin)}
helperText={
errors.profitMargin
? errors.profitMargin.message
: 'Your new profit margin will be applied in the next epoch'
}
required
muiTextFieldProps={{ fullWidth: true }}
sx={{ mb: 2.5 }}
/>
</form>
<Stack direction="row" justifyContent="space-between" mt={3}>
<Typography fontWeight={400}>Estimated operator reward for 10% PM</Typography>
<Typography fontWeight={400}>{`~${estimatedOpReward.amount} ${estimatedOpReward.denom}`}</Typography>
</Stack>
<Divider sx={{ my: 1 }} />
<Typography fontWeight={400}>Est. fee for this transaction will be cauculated in the next page</Typography>
</Box>
</SimpleDialog>
);
};
@@ -0,0 +1,47 @@
import * as React from 'react';
import { Divider, Stack, Typography } from '@mui/material';
import { MajorCurrencyAmount } from '@nymproject/types';
import { SimpleDialog } from '../../components';
export interface Props {
open: boolean;
onClose: () => void;
onConfirm: () => void;
onCancel: () => void;
currentPm: number;
newPm: number;
fee: MajorCurrencyAmount;
}
const SummaryModal = ({ open, onClose, onConfirm, onCancel, currentPm, newPm, fee }: Props) => (
<SimpleDialog
open={open}
onClose={onClose}
onConfirm={onConfirm}
onCancel={onCancel}
title="Profit margin change"
subTitle="System Variables"
confirmButton="Confirm"
closeButton
cancelButton
maxWidth="xs"
fullWidth
>
<Stack direction="row" justifyContent="space-between">
<Typography fontWeight={400}>Current profit margin</Typography>
<Typography fontWeight={400}>{`${currentPm}%`}</Typography>
</Stack>
<Divider sx={{ my: 1 }} />
<Stack direction="row" justifyContent="space-between">
<Typography fontWeight={400}>New profit margin</Typography>
<Typography fontWeight={400}>{`${newPm}%`}</Typography>
</Stack>
<Divider sx={{ my: 1 }} />
<Stack direction="row" justifyContent="space-between">
<Typography fontWeight={400}>Fee for this operation</Typography>
<Typography fontWeight={400}>{`${fee.amount} ${fee.denom}`}</Typography>
</Stack>
</SimpleDialog>
);
export default SummaryModal;
@@ -1,7 +1,8 @@
import { number, object } from 'yup';
const schema = object().shape({
profitMargin: number().required('Profit Percentage is required').min(0).max(100),
});
const getSchema = (currentPm: number) =>
object().shape({
profitMargin: number().required('Profit Percentage is required').min(0).max(100).notOneOf([currentPm]),
});
export default schema;
export default getSchema;