refactor(wallet): ui adjustments (#3182)

This commit is contained in:
Pierre Dommerc
2023-03-15 12:22:00 +01:00
committed by GitHub
parent 9eaf9cf491
commit 1010df1077
19 changed files with 261 additions and 100 deletions
@@ -22,12 +22,12 @@ export const MultiAccountWithPwdHowTo = ({ show, handleClose }: { show: boolean;
>
<Stack spacing={2}>
<Warning sx={{ textAlign: 'center' }}>
<Typography fontWeight={600} sx={{ mb: 1 }}>
<Typography variant="body2" fontWeight={600} sx={{ mb: 1 }}>
This machine already has a password set on it
</Typography>
<Typography>
<Typography variant="caption">
In order to import or create account(s) you need to log in with your password or create a new one. Creating a
new password will overwrite any old one. Make sure your menonics are all wirtten down before creating a new
new password will overwrite any old one. Make sure your mnemonics are all written down before creating a new
password.
</Typography>
</Warning>
@@ -35,13 +35,14 @@ export const MultiAccountWithPwdHowTo = ({ show, handleClose }: { show: boolean;
{passwordCreationSteps.map((step, index) => (
<Stack key={step} direction="row" spacing={1}>
<Typography fontWeight={600}>{`${index + 1}.`}</Typography>
<Typography>{`${step}`}</Typography>
<Typography variant="body2">{`${step}`}</Typography>
</Stack>
))}
<Link
href="https://nymtech.net/docs/stable/wallet#importing-or-creating-accounts-when-you-have-signed-in-with-mnemonic-but-a-password-already-exists-on-your-machine"
target="_blank"
text="Open Nym docs for this guide in a browser window"
variant="body2"
fontWeight={600}
/>
</Stack>
+14 -3
View File
@@ -1,8 +1,18 @@
import React, { useState } from 'react';
import { Alert as MuiAlert, IconButton } from '@mui/material';
import { Alert as MuiAlert, IconButton, SxProps } from '@mui/material';
import { Close } from '@mui/icons-material';
export const Alert = ({ title, dismissable }: { title: string | React.ReactNode; dismissable?: boolean }) => {
export const Alert = ({
title,
dismissable,
sxAlert,
bgColor,
}: {
title: string | React.ReactNode;
dismissable?: boolean;
sxAlert?: SxProps;
bgColor?: string;
}) => {
const [displayAlert, setDisplayAlert] = useState(true);
const handleDismiss = () => setDisplayAlert(false);
@@ -14,9 +24,10 @@ export const Alert = ({ title, dismissable }: { title: string | React.ReactNode;
sx={{
width: '100%',
borderRadius: 0,
bgcolor: 'background.default',
bgcolor: bgColor || 'background.default',
color: (theme) => theme.palette.nym.nymWallet.text.blue,
'& .MuiAlert-icon': { color: 'nym.nymWallet.text.blue', mr: 1 },
...sxAlert,
}}
action={
dismissable && (
+7 -1
View File
@@ -1,4 +1,5 @@
import React from 'react';
import { Link } from '@nymproject/react/link/Link';
import { Box, Button, Typography } from '@mui/material';
import { NymCard } from '../NymCard';
@@ -18,7 +19,12 @@ export const Bond = ({
justifyContent: 'space-between',
}}
>
<Typography variant="body2">Bond a mixnode or a gateway</Typography>
<Typography variant="body2">
Bond a mix node or a gateway. Learn how to set up and run a node{' '}
<Link href="https://nymtech.net/docs/nodes/setup-guides.html" target="_blank">
here
</Link>
</Typography>
<Box
sx={{
display: 'flex',
@@ -42,6 +42,7 @@ const GatewayInitForm = ({
initialValue={gatewayData?.identityKey}
errorText={errors.identityKey?.message}
onChanged={(value) => setValue('identityKey', value)}
showTickOnValid={false}
/>
<TextField
{...register('sphinxKey')}
@@ -36,6 +36,7 @@ const MixnodeInitForm = ({ mixnodeData, onNext }: { mixnodeData: MixnodeData; on
initialValue={mixnodeData?.identityKey}
errorText={errors.identityKey?.message}
onChanged={(value) => setValue('identityKey', value)}
showTickOnValid={false}
/>
<TextField
{...register('sphinxKey')}
+1 -1
View File
@@ -40,7 +40,7 @@ export const ClientAddressDisplay: FC<ClientAddressProps & { address?: string }>
)}
<AddressTooltip address={address} visible={!showEntireAddress}>
<Typography variant="body2" component="span" sx={{ mr: 1, color: 'text.primary', fontWeight: 400 }}>
<Typography variant="body2" component="span" sx={{ color: 'text.primary', fontWeight: 400 }}>
{showEntireAddress ? address || '' : splice(6, address)}
</Typography>
</AddressTooltip>
@@ -257,6 +257,7 @@ export const DelegateModal: FCWithChildren<{
textFieldProps={{
autoFocus: !initialIdentityKey,
}}
showTickOnValid={false}
/>
</Box>
<Typography
@@ -277,16 +278,9 @@ export const DelegateModal: FCWithChildren<{
autoFocus={Boolean(initialIdentityKey)}
onChanged={handleAmountChanged}
denom={denom}
validationError={errorAmount}
/>
</Box>
<Typography
component="div"
textAlign="left"
variant="caption"
sx={{ color: 'error.main', mx: 2, mt: errorAmount && 1 }}
>
{errorAmount}
</Typography>
<Box sx={{ mt: 3 }}>
<ModalListItem label="Account balance" value={accountBalance?.toUpperCase()} divider fontWeight={600} />
</Box>
@@ -0,0 +1,20 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { MockMainContextProvider } from '../context/mocks/main';
import { NetworkSelector } from './NetworkSelector';
export default {
title: 'Wallet / Network Selector',
component: NetworkSelector,
} as ComponentMeta<typeof NetworkSelector>;
const Template: ComponentStory<typeof NetworkSelector> = () => (
<Box mt={2} height={800}>
<MockMainContextProvider>
<NetworkSelector />
</MockMainContextProvider>
</Box>
);
export const Default = Template.bind({});
+25 -6
View File
@@ -1,6 +1,6 @@
import React, { useState, useContext } from 'react';
import { Button, List, ListItem, ListItemIcon, ListItemText, ListSubheader, Popover } from '@mui/material';
import { ArrowDropDown, CheckSharp } from '@mui/icons-material';
import { Button, List, ListItemButton, ListItemIcon, ListItemText, ListSubheader, Popover, Stack } from '@mui/material';
import { ArrowDropDown, Check } from '@mui/icons-material';
import { Network } from 'src/types';
import { AppContext } from '../context/main';
import { config } from '../config';
@@ -16,10 +16,29 @@ const NetworkItem: FCWithChildren<{ title: string; isSelected: boolean; onSelect
isSelected,
onSelect,
}) => (
<ListItem button onClick={onSelect}>
<ListItemIcon>{isSelected && <CheckSharp color="success" />}</ListItemIcon>
<ListItemText>{title}</ListItemText>
</ListItem>
<ListItemButton
onClick={onSelect}
sx={{
minWidth: '180px',
'&:hover': {
backgroundColor: isSelected ? 'rgba(251, 110, 78, 0.08) !important' : undefined,
},
}}
>
<Stack direction="row" justifyContent="space-between" alignItems="center" gap={2} width="100%">
<ListItemText
primaryTypographyProps={{
color: isSelected ? 'primary' : undefined,
}}
primary={title}
/>
{isSelected && (
<ListItemIcon sx={{ justifyContent: 'flex-end' }}>
<Check color="primary" fontSize="small" />
</ListItemIcon>
)}
</Stack>
</ListItemButton>
);
export const NetworkSelector = () => {
@@ -19,7 +19,6 @@ export const ReceiveModal = ({
return (
<SimpleModal
header="Receive"
subHeader="Provide your address to receive tokens"
open
onClose={onClose}
okLabel=""
@@ -128,7 +128,7 @@ export const SendInputModal = ({
<Stack gap={0.5} sx={{ mt: 1 }}>
<ModalListItem label="Account balance" value={balance?.toUpperCase()} divider fontWeight={600} />
<Typography fontSize="smaller" sx={{ color: 'text.primary' }}>
Est. fee for this transaction will be show on the next page
Est. fee for this transaction will be shown on the next page
</Typography>
</Stack>
<FormControlLabel
@@ -139,7 +139,7 @@ export const SendInputModal = ({
{showMore && (
<Stack direction="column" gap={3} mt={2} mb={3}>
<CurrencyFormField
label="Fees"
label="Fee"
onChanged={(v) => onUserFeesChange(v)}
initialValue={userFees?.amount}
fullWidth
+1 -1
View File
@@ -77,7 +77,7 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void
} catch (e) {
Console.error(e as string);
if (/Raw log: out of gas/.test(e as string)) {
setGasError('Out of gas, please increase the amount of fees');
setGasError('Specified fee was too small. Please increase the amount and try again');
} else {
setSendError(true);
}
@@ -0,0 +1,24 @@
import * as React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Box } from '@mui/material';
import { TokenPoolSelector } from './TokenPoolSelector';
import { MockMainContextProvider } from '../context/mocks/main';
export default {
title: 'Wallet / Token pool',
component: TokenPoolSelector,
} as ComponentMeta<typeof TokenPoolSelector>;
const Template: ComponentStory<typeof TokenPoolSelector> = (args) => (
<Box mt={2} height={800}>
<MockMainContextProvider>
<TokenPoolSelector {...args} />
</MockMainContextProvider>
</Box>
);
export const Default = Template.bind({});
Default.args = {
disabled: false,
onSelect: () => {},
};
+31 -12
View File
@@ -1,5 +1,15 @@
import React, { useContext, useEffect, useState } from 'react';
import { FormControl, InputLabel, ListItemText, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material';
import {
FormControl,
InputLabel,
ListItemText,
MenuItem,
Select,
SelectChangeEvent,
Stack,
Typography,
} from '@mui/material';
import { Check as CheckIcon } from '@mui/icons-material';
import { AppContext } from '../context/main';
export type TPoolOption = 'balance' | 'locked';
@@ -40,20 +50,29 @@ export const TokenPoolSelector: FCWithChildren<{ disabled: boolean; onSelect: (p
renderValue={(val) => <Typography sx={{ textTransform: 'capitalize' }}>{val}</Typography>}
>
<MenuItem value="balance">
<ListItemText
primary="Balance"
secondary={`${balance?.printable_balance}`}
secondaryTypographyProps={{ sx: { textTransform: 'uppercase' } }}
/>
<Stack direction="row" alignItems="center" gap={2} width="100%">
<ListItemText
primary="Balance"
secondary={`${balance?.printable_balance}`}
secondaryTypographyProps={{ sx: { textTransform: 'uppercase', color: 'nym.text.muted' } }}
/>
{value === 'balance' && <CheckIcon fontSize="small" />}
</Stack>
</MenuItem>
<MenuItem value="locked">
{tokenAllocation && (
<ListItemText
primary="Locked"
secondary={`${
+tokenAllocation.locked + +tokenAllocation.spendable
} ${clientDetails?.display_mix_denom.toUpperCase()}`}
/>
<Stack direction="row" alignItems="center" gap={2} width="100%">
<ListItemText
primary="Locked"
secondary={`${
+tokenAllocation.locked + +tokenAllocation.spendable
} ${clientDetails?.display_mix_denom.toUpperCase()}`}
secondaryTypographyProps={{
sx: { textTransform: 'uppercase', color: 'nym.text.muted' },
}}
/>
{value === 'locked' && <CheckIcon fontSize="small" />}
</Stack>
)}
</MenuItem>
</Select>
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { Box, Button, Divider, Grid, TextField, Typography } from '@mui/material';
import { Button, Divider, Grid, Stack, TextField, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { isMixnode } from 'src/types';
import { simulateUpdateMixnodeConfig, simulateVestingUpdateMixnodeConfig, updateMixnodeConfig } from 'src/requests';
@@ -62,7 +62,7 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon
};
return (
<Grid container xs item>
<Grid container xs>
{fee && (
<ConfirmTx
open
@@ -76,14 +76,17 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon
{isSubmitting && <LoadingModal />}
<Alert
title={
<Box sx={{ fontWeight: 600 }}>
Changing these values will ONLY change the data about your node on the blockchain. Remember to change your
nodes config file with the same values too
</Box>
<Stack>
<Typography fontWeight={600}>
Changing these values will ONLY change the data about your node on the blockchain.
</Typography>
<Typography>Remember to change your nodes config file with the same values too.</Typography>
</Stack>
}
bgColor={`${theme.palette.nym.nymWallet.text.blue}0D !important`}
dismissable
/>
<Grid container>
<Grid container mt={2}>
<Grid item container direction="row" alignItems="left" justifyContent="space-between" padding={3}>
<Grid item>
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1 }}>
@@ -126,7 +129,7 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon
</Grid>
</Grid>
</Grid>
<Divider flexItem />
<Divider sx={{ width: '100%' }} />
<Grid item container direction="row" alignItems="left" justifyContent="space-between" padding={3}>
<Grid item>
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1 }}>
@@ -147,7 +150,7 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon
</Grid>
</Grid>
</Grid>
<Divider flexItem />
<Divider sx={{ width: '100%' }} />
<Grid item container direction="row" alignItems="left" justifyContent="space-between" padding={3}>
<Grid item>
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1 }}>
@@ -168,31 +171,35 @@ export const InfoSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBon
</Grid>
</Grid>
</Grid>
<Divider flexItem />
<Grid container justifyContent="end">
<Button
size="large"
variant="contained"
disabled={isSubmitting || !isDirty || !isValid}
onClick={handleSubmit((data) =>
getFee(bondedNode.proxy ? simulateVestingUpdateMixnodeConfig : simulateUpdateMixnodeConfig, {
host: data.host,
mix_port: data.mixPort,
verloc_port: data.verlocPort,
http_api_port: data.httpApiPort,
version: data.version,
}),
)}
sx={{ m: 3 }}
>
Submit changes to the blockchain
</Button>
<Divider sx={{ width: '100%' }} />
<Grid item container direction="row" justifyContent="space-between" padding={3}>
<Grid item />
<Grid spacing={3} item container alignItems="center" xs={12} md={6}>
<Button
size="large"
variant="contained"
disabled={isSubmitting || !isDirty || !isValid}
onClick={handleSubmit((data) =>
getFee(bondedNode.proxy ? simulateVestingUpdateMixnodeConfig : simulateUpdateMixnodeConfig, {
host: data.host,
mix_port: data.mixPort,
verloc_port: data.verlocPort,
http_api_port: data.httpApiPort,
version: data.version,
}),
)}
sx={{ m: 3, mr: 0 }}
fullWidth
>
Submit changes to the blockchain
</Button>
</Grid>
</Grid>
</Grid>
<SimpleModal
open={openConfirmationModal}
header="Your changes are submitted to the blockchain"
subHeader="Remember to change the values
subHeader="Remember to change the values
on your nodes config file too."
okLabel="close"
hideCloseIcon
@@ -1,7 +1,18 @@
import React, { useContext, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { Box, Button, Divider, FormHelperText, Grid, InputAdornment, TextField, Typography } from '@mui/material';
import {
Box,
Button,
Divider,
FormHelperText,
Grid,
InputAdornment,
Stack,
TextField,
Tooltip,
Typography,
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { CurrencyDenom, MixNodeCostParams } from '@nymproject/types';
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
@@ -24,6 +35,12 @@ import { AppContext } from 'src/context';
import { useGetFee } from 'src/hooks/useGetFee';
import { ConfirmTx } from 'src/components/ConfirmTX';
import { LoadingModal } from 'src/components/Modals/LoadingModal';
import { InfoOutlined } from '@mui/icons-material';
const operatorCostHint = `This is your (operator) rewards including the PM and cost. Rewards are automatically compounded every epoch.You can redeem your rewards at any time.
`;
const profitMarginHint =
'PM is the percentage of the node rewards that you as the node operator take before rewards are distributed to the delegators.';
export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode }): JSX.Element => {
const [openConfirmationModal, setOpenConfirmationModal] = useState<boolean>(false);
@@ -114,10 +131,9 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
<Grid
container
xs
item
sx={{
'& .MuiGrid-item': {
pl: 0,
pl: 3,
},
}}
>
@@ -132,13 +148,29 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
/>
)}
{isSubmitting && <LoadingModal />}
<Alert title={<Box component="span" sx={{ fontWeight: 600 }}>{`Next interval: ${intervalTime}`}</Box>} />
<Grid container direction="column">
<Alert
title={<Typography sx={{ fontWeight: 600 }}>{`Next interval: ${intervalTime}`}</Typography>}
bgColor={`${theme.palette.nym.nymWallet.text.blue}0D !important`}
sxAlert={{
icon: false as unknown as number,
'& .MuiAlert-message': {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
},
}}
/>
<Grid container direction="column" mt={2}>
<Grid item container alignItems="left" justifyContent="space-between" padding={3} spacing={1}>
<Grid item xl={6}>
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1 }}>
Profit Margin
</Typography>
<Tooltip title={profitMarginHint} placement="top-end">
<Stack flexDirection="row" gap={0.5}>
<InfoOutlined fontSize="inherit" />
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1 }}>
Profit Margin
</Typography>
</Stack>
</Tooltip>
<Typography
variant="body1"
sx={{
@@ -180,12 +212,17 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
</Grid>
)}
</Grid>
<Divider flexItem sx={{ position: 'relative', left: '-24px', width: 'calc(100% + 24px)' }} />
<Divider sx={{ width: '100%' }} />
<Grid item container direction="row" alignItems="left" justifyContent="space-between" padding={3} spacing={1}>
<Grid item>
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1 }}>
Operating cost
</Typography>
<Tooltip title={operatorCostHint} placement="top-end">
<Stack flexDirection="row" gap={0.5}>
<InfoOutlined fontSize="inherit" />
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1 }}>
Operator cost
</Typography>
</Stack>
</Tooltip>
<Typography
variant="body1"
sx={{
@@ -201,7 +238,7 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
<CurrencyFormField
required
fullWidth
label="Operating cost"
label="Operator cost"
onChanged={(newValue) => {
setValue('operatorCost', newValue, { shouldValidate: true, shouldDirty: true });
}}
@@ -221,23 +258,27 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
)}
</Grid>
</Grid>
<Divider flexItem sx={{ position: 'relative', left: '-24px', width: 'calc(100% + 24px)' }} />
<Grid container justifyContent="end">
<Button
size="large"
variant="contained"
disabled={isSubmitting || !isDirty || !isValid}
onClick={handleSubmit((data) => {
getFee(bondedNode.proxy ? simulateVestingUpdateMixnodeCostParams : simulateUpdateMixnodeCostParams, {
profit_margin_percent: (+data.profitMargin / 100).toString(),
interval_operating_cost: data.operatorCost,
});
})}
type="submit"
sx={{ m: 3 }}
>
Submit changes to the blockchain
</Button>
<Divider sx={{ width: '100%' }} />
<Grid item container direction="row" justifyContent="space-between" padding={3}>
<Grid item />
<Grid item xs={12} md={6}>
<Button
size="large"
variant="contained"
disabled={isSubmitting || !isDirty || !isValid}
onClick={handleSubmit((data) => {
getFee(bondedNode.proxy ? simulateVestingUpdateMixnodeCostParams : simulateUpdateMixnodeCostParams, {
profit_margin_percent: (+data.profitMargin / 100).toString(),
interval_operating_cost: data.operatorCost,
});
})}
type="submit"
sx={{ m: 3, mr: 0, ml: 0 }}
fullWidth
>
Save changes
</Button>
</Grid>
</Grid>
</Grid>
<SimpleModal
@@ -5,7 +5,7 @@ import { TBondedMixnode, TBondedGateway } from '../../../../../context/bonding';
import { InfoSettings } from './InfoSettings';
import { ParametersSettings } from './ParametersSettings';
const nodeGeneralNav = ['Info', 'Parameters'];
const nodeGeneralNav = ['Node info', 'Parameters'];
export const NodeGeneralSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBondedGateway }) => {
const [settingsCard, setSettingsCard] = useState<string>(nodeGeneralNav[0]);
+18
View File
@@ -303,6 +303,24 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
},
},
},
MuiSelect: {
defaultProps: {
MenuProps: {
PaperProps: {
sx: {
'&& .Mui-selected': {
color: nymPalette.highlight,
backgroundColor: (t) =>
t.palette.mode === 'dark' ? `${t.palette.background.default} !important` : '#FFFFFF !important',
},
'&& .Mui-selected:hover': {
backgroundColor: 'rgba(251, 110, 78, 0.08) !important',
},
},
},
},
},
},
MuiMenu: {
styleOverrides: {
list: ({ _, theme }) => ({
@@ -84,7 +84,7 @@ export const CurrencyFormField: FCWithChildren<{
// it can't be lower than one micro coin
if (newNumber < MIN_VALUE) {
setValidationError('Amount cannot be less than 1 uNYM');
setValidationError('Amount cannot be less than 0.000001');
return fireOnValidate(false);
}