refactor to use accounts context

This commit is contained in:
fmtabbara
2022-05-04 16:21:29 +01:00
parent e0a80c777e
commit 6685b129bb
9 changed files with 143 additions and 231 deletions
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useContext } from 'react';
import {
Box,
IconButton,
@@ -10,48 +10,44 @@ import {
Typography,
} from '@mui/material';
import { Edit } from '@mui/icons-material';
import { AccountsContext } from 'src/context';
import { AccountAvatar } from './AccountAvatar';
import { ShowMnemonic } from './ShowMnemonic';
export const AccountItem = ({
name,
address,
isSelected,
onSelect,
onEdit,
}: {
name: string;
address: string;
isSelected: boolean;
onSelect: () => void;
onEdit: () => void;
}) => (
<ListItem disablePadding disableGutters sx={isSelected ? { bgcolor: 'rgba(33, 208, 115, 0.1)' } : {}}>
<ListItemButton disableRipple onClick={onSelect}>
<ListItemAvatar sx={{ minWidth: 0, mr: 2 }}>
<AccountAvatar name={name} address={address} />
</ListItemAvatar>
<ListItemText
primary={name}
secondary={
<Box>
<Typography variant="body2">{address}</Typography>
<Box sx={{ mt: 0.5 }}>
<ShowMnemonic accountName={name} />
export const AccountItem = ({ name, address }: { name: string; address: string }) => {
const { selectedAccount, handleSelectAccount, handleAccountToEdit } = useContext(AccountsContext);
return (
<ListItem
disablePadding
disableGutters
sx={selectedAccount?.id === name ? { bgcolor: 'rgba(33, 208, 115, 0.1)' } : {}}
>
<ListItemButton disableRipple onClick={() => handleSelectAccount(name)}>
<ListItemAvatar sx={{ minWidth: 0, mr: 2 }}>
<AccountAvatar name={name} />
</ListItemAvatar>
<ListItemText
primary={name}
secondary={
<Box>
<Typography variant="body2">{address}</Typography>
<Box sx={{ mt: 0.5 }}>
<ShowMnemonic accountName={name} />
</Box>
</Box>
</Box>
}
/>
<ListItemIcon>
<IconButton
onClick={(e) => {
e.stopPropagation();
onEdit();
}}
>
<Edit />
</IconButton>
</ListItemIcon>
</ListItemButton>
</ListItem>
);
}
/>
<ListItemIcon>
<IconButton
onClick={(e) => {
e.stopPropagation();
handleAccountToEdit(name);
}}
>
<Edit />
</IconButton>
</ListItemIcon>
</ListItemButton>
</ListItem>
);
};
+12 -73
View File
@@ -1,90 +1,29 @@
import React from 'react';
import React, { useContext } from 'react';
import { Button } from '@mui/material';
import { v4 as uuidv4 } from 'uuid';
import { AccountEntry } from 'src/types';
import { AccountsContext } from 'src/context';
import { EditAccountModal } from './EditAccountModal';
import { AddAccountModal } from './AddAccountModal';
import { AccountsModal } from './AccountsModal';
import { ImportAccountModal } from './ImportAccountModal';
import { AccountAvatar } from './AccountAvatar';
import { TDialog } from './types';
export const Accounts = ({
accounts,
selectedAccount,
accountToEdit,
dialogToDisplay,
addAccount,
editAccount,
setAccountToEdit,
importAccount,
setSelectedAccount,
setDialogToDisplay,
}: {
accounts?: AccountEntry[];
selectedAccount?: AccountEntry;
accountToEdit?: AccountEntry;
dialogToDisplay?: TDialog;
addAccount: (acc: { accountName: string; mnemonic: string; password: string }) => Promise<void>;
editAccount: (acc: AccountEntry) => void;
importAccount: (acc: AccountEntry & { mnemonic: string }) => void;
setAccountToEdit: (accountName: string) => void;
setSelectedAccount: (accountName: string) => void;
setDialogToDisplay: (dialog: TDialog | undefined) => void;
}) =>
accounts && selectedAccount ? (
export const Accounts = () => {
const { accounts, selectedAccount, setDialogToDisplay } = useContext(AccountsContext);
return accounts && selectedAccount ? (
<>
<Button
startIcon={<AccountAvatar address={selectedAccount.address} name={selectedAccount.id} />}
startIcon={<AccountAvatar name={selectedAccount.id} />}
sx={{ color: 'nym.text.dark' }}
onClick={() => setDialogToDisplay('Accounts')}
disableRipple
>
{selectedAccount.id}
</Button>
<AccountsModal
show={dialogToDisplay === 'Accounts'}
onClose={() => setDialogToDisplay(undefined)}
accounts={accounts}
onAccountSelect={(accountName) => setSelectedAccount(accountName)}
selectedAccount={selectedAccount.id}
onAdd={() => {
setDialogToDisplay('Add');
}}
onEdit={(accountName) => {
setAccountToEdit(accountName);
setDialogToDisplay('Edit');
}}
onImport={() => setDialogToDisplay('Import')}
/>
<AddAccountModal
show={dialogToDisplay === 'Add'}
onClose={() => {
setDialogToDisplay('Accounts');
}}
onAdd={async (data) => {
addAccount(data);
setDialogToDisplay('Accounts');
}}
/>
<EditAccountModal
show={dialogToDisplay === 'Edit'}
account={accountToEdit}
onClose={() => {
setDialogToDisplay('Accounts');
}}
onEdit={(account) => {
editAccount(account);
setDialogToDisplay('Accounts');
}}
/>
<ImportAccountModal
show={dialogToDisplay === 'Import'}
onClose={() => setDialogToDisplay('Accounts')}
onImport={(mnemonic) => {
importAccount({ id: 'New Account', address: uuidv4(), mnemonic });
setDialogToDisplay('Accounts');
}}
/>
<AccountsModal />
<AddAccountModal />
<EditAccountModal />
<ImportAccountModal />
</>
) : null;
};
@@ -1,62 +1,48 @@
import React from 'react';
import React, { useContext } from 'react';
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Typography } from '@mui/material';
import { Add, ArrowDownwardSharp, Close } from '@mui/icons-material';
import { AccountEntry } from 'src/types';
import { AccountsContext } from 'src/context';
import { AccountItem } from './AccountItem';
export const AccountsModal = ({
show,
accounts,
selectedAccount,
onClose,
onAccountSelect,
onAdd,
onEdit,
onImport,
}: {
show: boolean;
accounts: AccountEntry[];
selectedAccount: AccountEntry['id'];
onClose: () => void;
onAccountSelect: (accountName: string) => void;
onAdd: () => void;
onEdit: (accoutnName: string) => void;
onImport: () => void;
}) => (
<Dialog open={show} onClose={onClose} fullWidth hideBackdrop>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Accounts</Typography>
<IconButton onClick={onClose}>
<Close />
</IconButton>
</Box>
<Typography variant="body1" sx={{ color: 'grey.600' }}>
Switch between accounts
</Typography>
</DialogTitle>
<DialogContent sx={{ padding: 0 }}>
{accounts.map(({ id, address }) => (
<AccountItem
name={id}
address={address}
onSelect={() => {
onAccountSelect(id);
onClose();
}}
onEdit={() => onEdit(id)}
isSelected={selectedAccount === id}
key={address}
/>
))}
</DialogContent>
<DialogActions sx={{ p: 3 }}>
<Button startIcon={<ArrowDownwardSharp />} onClick={onImport}>
Import account
</Button>
<Button disableElevation variant="contained" startIcon={<Add fontSize="small" />} onClick={onAdd}>
Add new account
</Button>
</DialogActions>
</Dialog>
);
export const AccountsModal = ({ onClose }: { onClose?: () => void }) => {
const { accounts, dialogToDisplay, setDialogToDisplay } = useContext(AccountsContext);
const handleClose = () => {
setDialogToDisplay(undefined);
onClose?.();
};
return (
<Dialog open={dialogToDisplay === 'Accounts'} onClose={handleClose} fullWidth hideBackdrop>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Accounts</Typography>
<IconButton onClick={handleClose}>
<Close />
</IconButton>
</Box>
<Typography variant="body1" sx={{ color: 'grey.600' }}>
Switch between accounts
</Typography>
</DialogTitle>
<DialogContent sx={{ padding: 0 }}>
{accounts?.map(({ id, address }) => (
<AccountItem name={id} address={address} key={address} />
))}
</DialogContent>
<DialogActions sx={{ p: 3 }}>
<Button startIcon={<ArrowDownwardSharp />} onClick={() => setDialogToDisplay('Import')}>
Import account
</Button>
<Button
disableElevation
variant="contained"
startIcon={<Add fontSize="small" />}
onClick={() => setDialogToDisplay('Add')}
>
Add new account
</Button>
</DialogActions>
</Dialog>
);
};
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import {
Alert,
Box,
@@ -15,6 +15,7 @@ import {
import { Check, Close, ContentCopySharp } from '@mui/icons-material';
import { useClipboard } from 'use-clipboard-copy';
import { createMnemonic } from 'src/requests';
import { AccountsContext } from 'src/context';
const createAccountSteps = ['Save and copy mnemonic for your new account', 'Name your new account', 'Confirm password'];
@@ -135,42 +136,36 @@ const ConfirmPassword = ({ onConfirm }: { onConfirm: (password: string) => void
);
};
export const AddAccountModal = ({
show,
withoutPassword,
onClose,
onAdd,
}: {
show: boolean;
withoutPassword?: boolean;
onClose: () => void;
onAdd: (data: { accountName: string; mnemonic: string; password: string }) => void;
}) => {
export const AddAccountModal = ({ withoutPassword }: { withoutPassword?: boolean }) => {
const [step, setStep] = useState(0);
const [data, setData] = useState<{ mnemonic?: string; accountName?: string }>({
mnemonic: undefined,
accountName: undefined,
});
const { dialogToDisplay, setDialogToDisplay, handleAddAccount } = useContext(AccountsContext);
const generateMnemonic = async () => {
const mnemon = await createMnemonic();
setData((d) => ({ ...d, mnemonic: mnemon }));
};
const handleClose = () => {
setDialogToDisplay('Accounts');
setData({ mnemonic: undefined, accountName: undefined });
setStep(0);
};
useEffect(() => {
if (show) generateMnemonic();
else {
setData({ mnemonic: undefined, accountName: undefined });
setStep(0);
}
}, [show]);
if (dialogToDisplay === 'Accounts') generateMnemonic();
}, [dialogToDisplay]);
return (
<Dialog open={show} onClose={onClose} fullWidth hideBackdrop>
<Dialog open={dialogToDisplay === 'Add'} onClose={handleClose} fullWidth hideBackdrop>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Add new account</Typography>
<IconButton onClick={onClose}>
<IconButton onClick={handleClose}>
<Close />
</IconButton>
</Box>
@@ -181,7 +176,7 @@ export const AddAccountModal = ({
)}
<Typography sx={{ mt: 2 }}>{createAccountSteps[step]}</Typography>
</DialogTitle>
{withoutPassword && <NoPassword onClose={onClose} />}
{withoutPassword && <NoPassword onClose={handleClose} />}
{!withoutPassword &&
data.mnemonic &&
(() => {
@@ -202,7 +197,7 @@ export const AddAccountModal = ({
<ConfirmPassword
onConfirm={(password) => {
if (data.accountName && data.mnemonic) {
onAdd({ accountName: data.accountName, mnemonic: data.mnemonic, password });
handleAddAccount({ accountName: data.accountName, mnemonic: data.mnemonic, password });
}
}}
/>
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import {
Box,
Button,
@@ -11,31 +11,23 @@ import {
Typography,
} from '@mui/material';
import { Close } from '@mui/icons-material';
import { AccountEntry } from 'src/types';
import { AccountsContext } from 'src/context';
export const EditAccountModal = ({
account,
show,
onClose,
onEdit,
}: {
account?: AccountEntry;
show: boolean;
onClose: () => void;
onEdit: (account: AccountEntry) => void;
}) => {
export const EditAccountModal = () => {
const [accountName, setAccountName] = useState('');
const { accountToEdit, dialogToDisplay, setDialogToDisplay, handleEditAccount } = useContext(AccountsContext);
useEffect(() => {
setAccountName(account ? account?.id : '');
}, [account]);
setAccountName(accountToEdit ? accountToEdit?.id : '');
}, [accountToEdit]);
return (
<Dialog open={show} onClose={onClose} fullWidth hideBackdrop>
<Dialog open={dialogToDisplay === 'Edit'} onClose={() => setDialogToDisplay('Accounts')} fullWidth hideBackdrop>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Edit account name</Typography>
<IconButton onClick={onClose}>
<IconButton onClick={() => setDialogToDisplay('Accounts')}>
<Close />
</IconButton>
</Box>
@@ -60,7 +52,12 @@ export const EditAccountModal = ({
disableElevation
variant="contained"
size="large"
onClick={() => account && onEdit({ ...account, id: accountName })}
onClick={() => {
if (accountToEdit) {
handleEditAccount({ ...accountToEdit, id: accountName });
setDialogToDisplay('Accounts');
}
}}
disabled={!accountName?.length}
>
Edit
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useState } from 'react';
import {
Box,
Button,
@@ -11,28 +11,24 @@ import {
Typography,
} from '@mui/material';
import { Close } from '@mui/icons-material';
import { AccountsContext } from 'src/context';
export const ImportAccountModal = ({
show,
onClose,
onImport,
}: {
show: boolean;
onClose: () => void;
onImport: (mnemonic: string) => void;
}) => {
export const ImportAccountModal = () => {
const [mnemonic, setMnemonic] = useState('');
useEffect(() => {
if (!show) setMnemonic('');
}, [show]);
const { dialogToDisplay, setDialogToDisplay, handleImportAccount } = useContext(AccountsContext);
const handleClose = () => {
setMnemonic('');
setDialogToDisplay('Accounts');
};
return (
<Dialog open={show} onClose={onClose} fullWidth hideBackdrop>
<Dialog open={dialogToDisplay === 'Import'} onClose={handleClose} fullWidth hideBackdrop>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Import account</Typography>
<IconButton onClick={onClose}>
<IconButton onClick={handleClose}>
<Close />
</IconButton>
</Box>
@@ -59,7 +55,7 @@ export const ImportAccountModal = ({
disableElevation
variant="contained"
size="large"
onClick={() => onImport(mnemonic)}
onClick={() => handleImportAccount({ id: '', address: '' })}
disabled={!mnemonic.length}
>
Import account
+3 -1
View File
@@ -1,5 +1,5 @@
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { Account, AccountEntry, TAccountsDialog } from 'src/types';
import { AccountEntry } from 'src/types';
import { addAccount as addAccountRequest } from 'src/requests';
import { ClientContext } from './main';
@@ -16,6 +16,8 @@ type TAccounts = {
handleImportAccount: (account: AccountEntry) => void;
};
export type TAccountsDialog = 'Accounts' | 'Add' | 'Edit' | 'Import';
export const AccountsContext = createContext({} as TAccounts);
export const AccountsProvider: React.FC = ({ children }) => {
+1
View File
@@ -1,2 +1,3 @@
export * from './main';
export * from './sign-in';
export * from './accounts';
@@ -1,9 +1,9 @@
import React, { useContext, useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Box, Button, FormControl, Stack } from '@mui/material';
import { ClientContext } from 'src/context';
import { isPasswordCreated } from 'src/requests';
import { MnemonicInput, Subtitle } from '../components';
import { ClientContext } from 'src/context';
export const SignInMnemonic = () => {
const [mnemonic, setMnemonic] = useState('');