minor refactors

This commit is contained in:
fmtabbara
2022-05-06 15:54:38 +01:00
parent 9b0b961d43
commit 0e4787f078
10 changed files with 184 additions and 162 deletions
@@ -12,10 +12,10 @@ import {
import { Edit } from '@mui/icons-material';
import { AccountsContext } from 'src/context';
import { AccountAvatar } from './AccountAvatar';
import { ShowMnemonic } from './ShowMnemonic';
export const AccountItem = ({ name, address }: { name: string; address: string }) => {
const { selectedAccount, handleSelectAccount, handleAccountToEdit } = useContext(AccountsContext);
const { selectedAccount, handleSelectAccount, handleAccountToEdit, setDialogToDisplay, setAccountMnemonic } =
useContext(AccountsContext);
return (
<ListItem
disablePadding
@@ -32,7 +32,18 @@ export const AccountItem = ({ name, address }: { name: string; address: string }
<Box>
<Typography variant="body2">{address}</Typography>
<Box sx={{ mt: 0.5 }}>
<ShowMnemonic accountName={name} />
<Typography
variant="body2"
component="span"
sx={{ textDecoration: 'underline', mb: 0.5, '&:hover': { color: 'primary.main' } }}
onClick={(e: React.MouseEvent<HTMLElement>) => {
e.stopPropagation();
setDialogToDisplay('Mnemonic');
setAccountMnemonic((accountMnemonic) => ({ ...accountMnemonic, accountName: name }));
}}
>
Show mnemonic
</Typography>
</Box>
</Box>
}
@@ -5,6 +5,7 @@ import { EditAccountModal } from './EditAccountModal';
import { AddAccountModal } from './AddAccountModal';
import { AccountsModal } from './AccountsModal';
import { AccountAvatar } from './AccountAvatar';
import { MnemonicModal } from './MnemonicModal';
export const Accounts = () => {
const { accounts, selectedAccount, setDialogToDisplay } = useContext(AccountsContext);
@@ -22,6 +23,7 @@ export const Accounts = () => {
<AccountsModal />
<AddAccountModal />
<EditAccountModal />
<MnemonicModal />
</>
) : null;
};
@@ -17,6 +17,7 @@ import { Check, Close, ContentCopySharp } from '@mui/icons-material';
import { useClipboard } from 'use-clipboard-copy';
import { createMnemonic } from 'src/requests';
import { AccountsContext } from 'src/context';
import { Mnemonic } from '../Mnemonic';
const createAccountSteps = [
'Copy and save mnemonic for your new account',
@@ -29,67 +30,12 @@ const importAccountSteps = [
'Confirm the password used to login to your wallet',
];
const passwordCreationSteps = [
'Log out',
'During sign in screen click “Sign in with mnemonic” button',
'On next screen click “Create a password for your account”',
'Sign in to wallet with your new password',
'Now you can create multiple accounts',
];
const NoPassword = ({ onClose }: { onClose: () => void }) => (
<Box sx={{ mt: 1 }}>
<DialogContent>
<Stack spacing={2}>
<Alert severity="warning" icon={false} sx={{ display: 'block' }}>
<Typography sx={{ textAlign: 'center' }}>
You cant add new accounts if your wallet doesnt have a password.
</Typography>
<Typography sx={{ textAlign: 'center' }}>Follow steps below to create password.</Typography>
</Alert>
<Typography variant="h6">How to create password to your account</Typography>
{passwordCreationSteps.map((step, i) => (
<Typography>{`${i + 1}. ${step}`}</Typography>
))}
</Stack>
</DialogContent>
<DialogActions sx={{ p: 3 }}>
<Button fullWidth disableElevation variant="contained" size="large" onClick={onClose}>
OK
</Button>
</DialogActions>
</Box>
);
const MnemonicStep = ({ mnemonic, onNext }: { mnemonic: string; onNext: () => void }) => {
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
return (
<Box sx={{ mt: 1 }}>
<DialogContent>
<Stack spacing={2} alignItems="center">
<Alert severity="warning" icon={false} sx={{ display: 'block' }}>
<Typography sx={{ textAlign: 'center' }}>
Below is your 24 word mnemonic, make sure to store it in a safe place for accessing your wallet in the
future
</Typography>
</Alert>
<TextField multiline rows={3} value={mnemonic} fullWidth />
<Button
color="inherit"
disableElevation
size="large"
onClick={() => {
copy(mnemonic);
}}
sx={{
width: 250,
}}
endIcon={!copied ? <ContentCopySharp /> : <Check color="success" />}
>
Copy mnemonic
</Button>
</Stack>
<Mnemonic mnemonic={mnemonic} handleCopy={copy} copied={copied} />
</DialogContent>
<DialogActions sx={{ p: 3, pt: 0 }}>
<Button disabled={!copied} fullWidth disableElevation variant="contained" size="large" onClick={onNext}>
@@ -0,0 +1,102 @@
import React, { useContext, useState } from 'react';
import {
Box,
Button,
CircularProgress,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
TextField,
Typography,
} from '@mui/material';
import { Close } from '@mui/icons-material';
import { AccountsContext } from 'src/context';
import { useClipboard } from 'use-clipboard-copy';
import { Mnemonic } from '../Mnemonic';
export const MnemonicModal = () => {
const [password, setPassword] = useState('');
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
const {
dialogToDisplay,
setDialogToDisplay,
accountMnemonic,
setAccountMnemonic,
handleGetAcccountMnemonic,
error,
setError,
isLoading,
} = useContext(AccountsContext);
const handleClose = () => {
setAccountMnemonic({ value: undefined, accountName: undefined });
setError(undefined);
setDialogToDisplay('Accounts');
setPassword('');
};
return (
<Dialog open={dialogToDisplay === 'Mnemonic'} onClose={handleClose} fullWidth hideBackdrop>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Display mnemonic</Typography>
<IconButton onClick={handleClose}>
<Close />
</IconButton>
</Box>
<Typography variant="body1" sx={{ color: 'grey.600' }}>
{`Display mnemonic for: ${accountMnemonic?.accountName}`}
</Typography>
</DialogTitle>
<DialogContent sx={{ p: 0 }}>
<Box sx={{ px: 3, mt: 1 }}>
{error && (
<Typography variant="body1" sx={{ color: 'error.main', mb: 2 }}>
{error}
</Typography>
)}
{!accountMnemonic.value ? (
<>
<Typography sx={{ mb: 2 }}>Enter the password used to login to your wallet</Typography>
<TextField
label="Password"
type="password"
fullWidth
value={password}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPassword(e.target.value)}
autoFocus
/>
</>
) : (
<Mnemonic mnemonic={accountMnemonic.value} handleCopy={copy} copied={copied} />
)}
</Box>
</DialogContent>
<DialogActions sx={{ p: 3 }}>
{!accountMnemonic.value && (
<Button
disableRipple
disabled={!password.length || isLoading}
fullWidth
disableElevation
variant="contained"
size="large"
onClick={async () => {
if (accountMnemonic?.accountName) {
setError(undefined);
await handleGetAcccountMnemonic({ password, accountName: accountMnemonic?.accountName });
}
}}
endIcon={isLoading && <CircularProgress size={20} />}
>
Display mnemonic
</Button>
)}
</DialogActions>
</Dialog>
);
};
@@ -1,29 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Box, Typography } from '@mui/material';
import { CopyToClipboard } from '@nymproject/react';
export const ShowMnemonic = ({ accountName }: { accountName: string }) => {
const [showMnemonic, setShowMnemonic] = useState<string>();
const [mnemonic, setMnemonic] = useState<string>();
return (
<Box>
<Typography
variant="body2"
sx={{ textDecoration: 'underline', mb: 0.5 }}
onClick={(e) => {
e.stopPropagation();
setShowMnemonic((show) => (!show ? accountName : undefined));
}}
>
{`${showMnemonic ? 'Hide' : 'Show'} mnemonic`}
</Typography>
{mnemonic && (
<Box display="flex" alignItems="end">
<Typography variant="caption">{mnemonic}</Typography>
<CopyToClipboard sx={{ width: 18 }} value={mnemonic} />
</Box>
)}
</Box>
);
};
@@ -1,47 +0,0 @@
import React from 'react';
import {
Alert,
AlertTitle,
Box,
Dialog,
DialogContent,
DialogTitle,
IconButton,
Stack,
Typography,
} from '@mui/material';
import { Close } from '@mui/icons-material';
import { CopyToClipboard } from '@nymproject/react';
export const ShowMnemonicModal = ({
mnemonic,
show,
onClose,
}: {
mnemonic: string;
show: boolean;
onClose: () => void;
}) => (
<Dialog open={show} onClose={onClose} fullWidth hideBackdrop>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Show mnemonic</Typography>
<IconButton onClick={onClose}>
<Close />
</IconButton>
</Box>
</DialogTitle>
<DialogContent>
<Stack spacing={2}>
<Alert severity="warning" icon={false} sx={{ display: 'block' }}>
<Typography sx={{ textAlign: 'center' }}>DO NOT share this phrase with anyone!</Typography>
<Typography sx={{ textAlign: 'center' }}>These words can be used to steal all your accounts.</Typography>
</Alert>
<Alert color="info" action={<CopyToClipboard value={mnemonic} />} icon={false}>
<AlertTitle sx={{ fontWeight: 700 }}>Mnemonic</AlertTitle>
<Typography variant="body1">{mnemonic}</Typography>
</Alert>
</Stack>
</DialogContent>
</Dialog>
);
@@ -1,23 +0,0 @@
import React from 'react';
import { Box } from '@mui/material';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { ShowMnemonicModal } from 'src/components/Accounts/ShowMnemonicModal';
export default {
title: 'Wallet / Multi Account / Show Mnemonic',
component: ShowMnemonicModal,
} as ComponentMeta<typeof ShowMnemonicModal>;
const Template: ComponentStory<typeof ShowMnemonicModal> = (args) => (
<Box display="flex" alignContent="center">
<ShowMnemonicModal {...args} />
</Box>
);
export const Default = Template.bind({});
Default.args = {
mnemonic:
'lonely employ curtain skull gas swim pizza injury tail birth inmate apart giraffe behave caution hammer echo action best symptom skull toast beyond casino',
show: true,
onClose: () => {},
};
+37
View File
@@ -0,0 +1,37 @@
import React from 'react';
import { Alert, Button, Stack, TextField, Typography } from '@mui/material';
import { Check, ContentCopySharp } from '@mui/icons-material';
export const Mnemonic = ({
mnemonic,
copied,
handleCopy,
}: {
mnemonic: string;
copied: boolean;
handleCopy: (text?: string) => void;
}) => (
<Stack spacing={2} alignItems="center">
<Alert severity="warning" icon={false} sx={{ display: 'block' }}>
<Typography sx={{ textAlign: 'center' }}>
Below is your 24 word mnemonic, make sure to store it in a safe place for accessing your wallet in the future
</Typography>
</Alert>
<TextField multiline rows={3} value={mnemonic} fullWidth />
<Button
color="inherit"
disableElevation
size="large"
onClick={() => {
handleCopy(mnemonic);
}}
sx={{
width: 250,
}}
endIcon={!copied ? <ContentCopySharp /> : <Check color="success" />}
>
Copy mnemonic
</Button>
</Stack>
);
+26 -3
View File
@@ -1,6 +1,6 @@
import React, { createContext, Dispatch, SetStateAction, useContext, useEffect, useMemo, useState } from 'react';
import { AccountEntry } from 'src/types';
import { addAccount as addAccountRequest } from 'src/requests';
import { addAccount as addAccountRequest, showMnemonicForAccount } from 'src/requests';
import { useSnackbar } from 'notistack';
import { ClientContext } from './main';
@@ -11,16 +11,20 @@ type TAccounts = {
dialogToDisplay?: TAccountsDialog;
isLoading: boolean;
error?: string;
accountMnemonic: TAccountMnemonic;
setError: Dispatch<SetStateAction<string | undefined>>;
setAccountMnemonic: Dispatch<SetStateAction<TAccountMnemonic>>;
handleAddAccount: (data: { accountName: string; mnemonic: string; password: string }) => void;
setDialogToDisplay: (dialog?: TAccountsDialog) => void;
handleSelectAccount: (accountId: string) => void;
handleAccountToEdit: (accountId: string) => void;
handleEditAccount: (account: AccountEntry) => void;
handleImportAccount: (account: AccountEntry) => void;
handleGetAcccountMnemonic: (data: { password: string; accountName: string }) => void;
};
export type TAccountsDialog = 'Accounts' | 'Add' | 'Edit' | 'Import';
export type TAccountsDialog = 'Accounts' | 'Add' | 'Edit' | 'Import' | 'Mnemonic';
export type TAccountMnemonic = { value?: string; accountName?: string };
export const AccountsContext = createContext({} as TAccounts);
@@ -29,6 +33,10 @@ export const AccountsProvider: React.FC = ({ children }) => {
const [selectedAccount, setSelectedAccount] = useState<AccountEntry>();
const [accountToEdit, setAccountToEdit] = useState<AccountEntry>();
const [dialogToDisplay, setDialogToDisplay] = useState<TAccountsDialog>();
const [accountMnemonic, setAccountMnemonic] = useState<TAccountMnemonic>({
value: undefined,
accountName: undefined,
});
const [error, setError] = useState<string>();
const [isLoading, setIsLoading] = useState(false);
const { onAccountChange, storedAccounts } = useContext(ClientContext);
@@ -73,6 +81,18 @@ export const AccountsProvider: React.FC = ({ children }) => {
setSelectedAccount(match);
};
const handleGetAcccountMnemonic = async ({ password, accountName }: { password: string; accountName: string }) => {
try {
setIsLoading(true);
const mnemonic = await showMnemonicForAccount({ password, accountName });
setAccountMnemonic({ value: mnemonic, accountName });
} catch (e) {
setError(e as string);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
if (storedAccounts) {
setAccounts(storedAccounts);
@@ -93,15 +113,18 @@ export const AccountsProvider: React.FC = ({ children }) => {
selectedAccount,
accountToEdit,
dialogToDisplay,
accountMnemonic,
setDialogToDisplay,
setAccountMnemonic,
isLoading,
handleAddAccount,
handleEditAccount,
handleAccountToEdit,
handleSelectAccount,
handleImportAccount,
handleGetAcccountMnemonic,
}),
[accounts, selectedAccount, accountToEdit, dialogToDisplay, isLoading, error],
[accounts, selectedAccount, accountToEdit, dialogToDisplay, isLoading, error, accountMnemonic],
)}
>
{children}
+1 -1
View File
@@ -58,7 +58,7 @@ export const listAccounts = async () => {
};
export const showMnemonicForAccount = async ({ password, accountName }: { password: string; accountName: string }) => {
const res: string = await invoke('show_mnemonic_for_account_in_password', { password, innerId: accountName });
const res: string = await invoke('show_mnemonic_for_account_in_password', { password, accountId: accountName });
return res;
};