-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1635 from oasisprotocol/mz/addr
Address Book
- Loading branch information
Showing
23 changed files
with
469 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
src/app/components/Toolbar/Features/Contacts/AddContact.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { useContext } from 'react' | ||
import { useDispatch } from 'react-redux' | ||
import { useTranslation } from 'react-i18next' | ||
import { Box } from 'grommet/es6/components/Box' | ||
import { Tabs } from 'grommet/es6/components/Tabs' | ||
import { Tab } from 'grommet/es6/components/Tab' | ||
import { ResponsiveContext } from 'grommet/es6/contexts/ResponsiveContext' | ||
import { contactsActions } from 'app/state/contacts' | ||
import { Contact } from 'app/state/contacts/types' | ||
import { ResponsiveLayer } from '../../../ResponsiveLayer' | ||
import { ContactAccountForm } from './ContactAccountForm' | ||
import { layerOverlayMinHeight } from './layer' | ||
|
||
interface AddContactProps { | ||
setLayerVisibility: (isVisible: boolean) => void | ||
} | ||
|
||
export const AddContact = ({ setLayerVisibility }: AddContactProps) => { | ||
const { t } = useTranslation() | ||
const isMobile = useContext(ResponsiveContext) === 'small' | ||
const dispatch = useDispatch() | ||
const submitHandler = (contact: Contact) => dispatch(contactsActions.add(contact)) | ||
|
||
return ( | ||
<ResponsiveLayer | ||
onClickOutside={() => setLayerVisibility(false)} | ||
onEsc={() => setLayerVisibility(false)} | ||
animation="none" | ||
background="background-front" | ||
modal | ||
position="top" | ||
margin={isMobile ? 'none' : 'xlarge'} | ||
> | ||
<Box margin="medium" width={isMobile ? 'auto' : '700px'}> | ||
<Tabs alignControls="start"> | ||
<Tab title={t('toolbar.contacts.add', 'Add Contact')} style={{ textTransform: 'capitalize' }}> | ||
<Box | ||
flex="grow" | ||
justify="center" | ||
height={{ min: isMobile ? 'auto' : layerOverlayMinHeight }} | ||
pad={{ vertical: 'medium' }} | ||
> | ||
<ContactAccountForm | ||
onCancel={() => setLayerVisibility(false)} | ||
onSave={contract => { | ||
submitHandler(contract) | ||
setLayerVisibility(false) | ||
}} | ||
/> | ||
</Box> | ||
</Tab> | ||
</Tabs> | ||
</Box> | ||
</ResponsiveLayer> | ||
) | ||
} |
80 changes: 80 additions & 0 deletions
80
src/app/components/Toolbar/Features/Contacts/ContactAccount.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { useContext, useState } from 'react' | ||
import { useDispatch } from 'react-redux' | ||
import { useTranslation } from 'react-i18next' | ||
import { Box } from 'grommet/es6/components/Box' | ||
import { ResponsiveContext } from 'grommet/es6/contexts/ResponsiveContext' | ||
import { Tabs } from 'grommet/es6/components/Tabs' | ||
import { Tab } from 'grommet/es6/components/Tab' | ||
import { contactsActions } from 'app/state/contacts' | ||
import { Contact } from 'app/state/contacts/types' | ||
import { Account } from '../Account/Account' | ||
import { ResponsiveLayer } from '../../../ResponsiveLayer' | ||
import { ContactAccountForm } from './ContactAccountForm' | ||
import { layerOverlayMinHeight } from './layer' | ||
|
||
interface ContactAccountProps { | ||
contact: Contact | ||
} | ||
|
||
export const ContactAccount = ({ contact }: ContactAccountProps) => { | ||
const { t } = useTranslation() | ||
const [layerVisibility, setLayerVisibility] = useState(false) | ||
const isMobile = useContext(ResponsiveContext) === 'small' | ||
const dispatch = useDispatch() | ||
const submitHandler = (contact: Contact) => dispatch(contactsActions.update(contact)) | ||
const deleteHandler = (address: string) => dispatch(contactsActions.delete(address)) | ||
|
||
return ( | ||
<Box> | ||
<Account | ||
address={contact.address} | ||
balance={undefined} | ||
displayBalance={false} | ||
displayManageButton={{ | ||
onClickManage: () => setLayerVisibility(true), | ||
}} | ||
isActive={false} | ||
key={contact.address} | ||
name={contact.name} | ||
onClick={() => setLayerVisibility(true)} | ||
/> | ||
{layerVisibility && ( | ||
<ResponsiveLayer | ||
onClickOutside={() => setLayerVisibility(false)} | ||
onEsc={() => setLayerVisibility(false)} | ||
animation="none" | ||
background="background-front" | ||
modal | ||
position="top" | ||
margin={isMobile ? 'none' : 'xlarge'} | ||
> | ||
<Box margin="medium" width={isMobile ? 'auto' : '700px'}> | ||
<Tabs alignControls="start"> | ||
<Tab title={t('toolbar.contacts.manage', 'Manage Contact')}> | ||
<Box | ||
flex="grow" | ||
justify="center" | ||
height={{ min: isMobile ? 'auto' : layerOverlayMinHeight }} | ||
pad={{ vertical: 'medium' }} | ||
> | ||
<ContactAccountForm | ||
contact={contact} | ||
onDelete={address => { | ||
deleteHandler(address) | ||
setLayerVisibility(false) | ||
}} | ||
onCancel={() => setLayerVisibility(false)} | ||
onSave={contract => { | ||
submitHandler(contract) | ||
setLayerVisibility(false) | ||
}} | ||
/> | ||
</Box> | ||
</Tab> | ||
</Tabs> | ||
</Box> | ||
</ResponsiveLayer> | ||
)} | ||
</Box> | ||
) | ||
} |
111 changes: 111 additions & 0 deletions
111
src/app/components/Toolbar/Features/Contacts/ContactAccountForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { useState } from 'react' | ||
import { useSelector } from 'react-redux' | ||
import { useTranslation } from 'react-i18next' | ||
import { Box } from 'grommet/es6/components/Box' | ||
import { Button } from 'grommet/es6/components/Button' | ||
import { Form } from 'grommet/es6/components/Form' | ||
import { FormField } from 'grommet/es6/components/FormField' | ||
import { TextInput } from 'grommet/es6/components/TextInput' | ||
import { TextArea } from 'grommet/es6/components/TextArea' | ||
import { selectContactsList } from 'app/state/contacts/selectors' | ||
import { isValidAddress } from 'app/lib/helpers' | ||
import { Contact } from 'app/state/contacts/types' | ||
import { DeleteContact } from './DeleteContact' | ||
|
||
interface ContactAccountFormProps { | ||
contact?: Contact | ||
onDelete?: (address: string) => void | ||
onCancel: () => void | ||
onSave: (contact: Contact) => void | ||
} | ||
|
||
interface FormValue { | ||
address: string | ||
name: string | ||
} | ||
|
||
export const ContactAccountForm = ({ contact, onDelete, onCancel, onSave }: ContactAccountFormProps) => { | ||
const { t } = useTranslation() | ||
const [deleteLayerVisibility, setDeleteLayerVisibility] = useState(false) | ||
const [value, setValue] = useState({ name: contact?.name || '', address: contact?.address || '' }) | ||
const contacts = useSelector(selectContactsList) | ||
|
||
return ( | ||
<Form<FormValue> | ||
style={{ display: 'flex', flex: 1, flexDirection: 'column' }} | ||
messages={{ required: t('toolbar.contacts.validation.required', 'Field is required') }} | ||
onChange={nextValue => setValue(nextValue)} | ||
onSubmit={({ value }) => | ||
onSave({ address: value.address.replaceAll(' ', ''), name: value.name.trim() }) | ||
} | ||
value={value} | ||
> | ||
<Box flex="grow"> | ||
<FormField | ||
name="name" | ||
required | ||
validate={(name: string) => | ||
name.trim().length > 16 | ||
? { | ||
message: t('toolbar.contacts.validation.nameLengthError', 'No more than 16 characters'), | ||
status: 'error', | ||
} | ||
: undefined | ||
} | ||
> | ||
<TextInput name="name" placeholder={t('toolbar.contacts.name', 'Name')} /> | ||
</FormField> | ||
<FormField | ||
disabled={!!contact} | ||
name="address" | ||
required | ||
validate={(address: string) => | ||
!isValidAddress(address.replaceAll(' ', '')) | ||
? { | ||
message: t( | ||
'toolbar.contacts.validation.addressError', | ||
'Please enter a valid wallet address', | ||
), | ||
status: 'error', | ||
} | ||
: !contact && contacts.find(contact => contact.address === address.replaceAll(' ', '')) | ||
? { | ||
message: t('toolbar.contacts.validation.addressNotUniqueError', 'Address already exists'), | ||
status: 'error', | ||
} | ||
: undefined | ||
} | ||
> | ||
<TextArea | ||
disabled={!!contact} | ||
rows={3} | ||
name="address" | ||
placeholder={t('toolbar.contacts.address', 'Address')} | ||
/> | ||
</FormField> | ||
{contact && onDelete && ( | ||
<Box align="end"> | ||
<Button | ||
style={{ fontSize: '14px', fontWeight: 600 }} | ||
margin={{ vertical: 'medium' }} | ||
color="status-error" | ||
label={t('toolbar.contacts.delete.button', 'Delete contact')} | ||
onClick={() => setDeleteLayerVisibility(true)} | ||
plain | ||
></Button> | ||
{deleteLayerVisibility && ( | ||
<DeleteContact | ||
onDelete={() => onDelete(contact.address)} | ||
onCancel={() => setDeleteLayerVisibility(false)} | ||
/> | ||
)} | ||
</Box> | ||
)} | ||
</Box> | ||
<Box direction="row" align="center" justify="between" pad={{ top: 'medium' }}> | ||
<Button label={t('toolbar.contacts.cancel', 'Cancel')} onClick={onCancel} /> | ||
<Button type="submit" label={t('toolbar.contacts.save', 'Save')} primary /> | ||
</Box> | ||
</Form> | ||
) | ||
} |
48 changes: 48 additions & 0 deletions
48
src/app/components/Toolbar/Features/Contacts/DeleteContact.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { useContext } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import { Box } from 'grommet/es6/components/Box' | ||
import { Button } from 'grommet/es6/components/Button' | ||
import { ResponsiveContext } from 'grommet/es6/contexts/ResponsiveContext' | ||
import { Text } from 'grommet/es6/components/Text' | ||
import { ResponsiveLayer } from '../../../ResponsiveLayer' | ||
|
||
interface DeleteContactProps { | ||
onDelete: () => void | ||
onCancel: () => void | ||
} | ||
|
||
export const DeleteContact = ({ onCancel, onDelete }: DeleteContactProps) => { | ||
const { t } = useTranslation() | ||
const isMobile = useContext(ResponsiveContext) === 'small' | ||
|
||
return ( | ||
<ResponsiveLayer | ||
onClickOutside={onCancel} | ||
onEsc={onCancel} | ||
animation="none" | ||
background="background-front" | ||
modal | ||
margin={isMobile ? 'none' : 'xlarge'} | ||
> | ||
<Box margin="medium"> | ||
<Box flex="grow" justify="center"> | ||
<Text weight="bold" size="medium" alignSelf="center" margin={{ bottom: 'large' }}> | ||
{t('toolbar.contacts.delete.title', 'Delete Contact')} | ||
</Text> | ||
<Text size="medium" alignSelf="center" margin={{ bottom: 'medium' }}> | ||
{t('toolbar.contacts.delete.description', 'Are you sure you want to delete this contact?')} | ||
</Text> | ||
</Box> | ||
<Box direction="row" align="center" justify="between" pad={{ top: 'medium' }}> | ||
<Button label={t('toolbar.contacts.cancel', 'Cancel')} onClick={onCancel} /> | ||
<Button | ||
onClick={onDelete} | ||
label={t('toolbar.contacts.delete.confirm', 'Yes, delete')} | ||
color="status-error" | ||
primary | ||
/> | ||
</Box> | ||
</Box> | ||
</ResponsiveLayer> | ||
) | ||
} |
Oops, something went wrong.