Commit 146e347f authored by tom's avatar tom

base implementation

parent db5f2ae7
import React from 'react';
// prevent set focus on button when closing modal
export default function usePreventFocusAfterModalClosing() {
return React.useCallback((e: React.SyntheticEvent) => e.stopPropagation(), []);
}
...@@ -11,7 +11,6 @@ import { QueryKeys } from 'types/client/queries'; ...@@ -11,7 +11,6 @@ import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import metamaskIcon from 'icons/metamask.svg'; import metamaskIcon from 'icons/metamask.svg';
import qrCodeIcon from 'icons/qr_code.svg'; import qrCodeIcon from 'icons/qr_code.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import walletIcon from 'icons/wallet.svg'; import walletIcon from 'icons/wallet.svg';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -21,6 +20,8 @@ import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; ...@@ -21,6 +20,8 @@ import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import ExternalLink from 'ui/shared/ExternalLink'; import ExternalLink from 'ui/shared/ExternalLink';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import AddressFavoriteButton from './details/AddressFavoriteButton';
interface Props { interface Props {
addressQuery: UseQueryResult<TAddress>; addressQuery: UseQueryResult<TAddress>;
} }
...@@ -65,9 +66,7 @@ const AddressDetails = ({ addressQuery }: Props) => { ...@@ -65,9 +66,7 @@ const AddressDetails = ({ addressQuery }: Props) => {
</Text> </Text>
<CopyToClipboard text={ addressQuery.data.hash }/> <CopyToClipboard text={ addressQuery.data.hash }/>
<Icon as={ metamaskIcon } boxSize={ 6 } ml={ 2 }/> <Icon as={ metamaskIcon } boxSize={ 6 } ml={ 2 }/>
<Button variant="outline" size="sm" ml={ 3 }> <AddressFavoriteButton hash={ addressQuery.data.hash } isAdded={ Boolean(addressQuery.data.watchlist_names?.length) } ml={ 3 }/>
<Icon as={ starOutlineIcon } boxSize={ 5 }/>
</Button>
<Button variant="outline" size="sm" ml={ 2 }> <Button variant="outline" size="sm" ml={ 2 }>
<Icon as={ qrCodeIcon } boxSize={ 5 }/> <Icon as={ qrCodeIcon } boxSize={ 5 }/>
</Button> </Button>
......
import { Icon, chakra, Tooltip, IconButton, useDisclosure } from '@chakra-ui/react';
import React from 'react';
import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
import WatchlistAddModal from 'ui/watchlist/AddressModal/AddressModal';
import DeleteAddressModal from 'ui/watchlist/DeleteAddressModal';
interface Props {
className?: string;
hash: string;
isAdded: boolean;
}
const AddressFavoriteButton = ({ className, hash, isAdded }: Props) => {
const addModalProps = useDisclosure();
const deleteModalProps = useDisclosure();
const handleClick = React.useCallback(() => {
isAdded ? deleteModalProps.onOpen() : addModalProps.onOpen();
}, [ addModalProps, deleteModalProps, isAdded ]);
const handleAddModalClose = React.useCallback(() => {
addModalProps.onClose();
}, [ addModalProps ]);
const handleDeleteModalClose = React.useCallback(() => {
deleteModalProps.onClose();
}, [ deleteModalProps ]);
const formData = React.useMemo(() => {
return { address_hash: hash, id: '1' };
}, [ hash ]);
return (
<>
<Tooltip label={ `${ isAdded ? 'Remove address from Watch list' : 'Add address to Watch list' }` }>
<IconButton
className={ className }
aria-label="edit"
variant="outline"
size="sm"
pl={ 2 }
pr={ 2 }
onClick={ handleClick }
icon={ <Icon as={ isAdded ? starFilledIcon : starOutlineIcon } boxSize={ 5 }/> }
onFocusCapture={ usePreventFocusAfterModalClosing() }
/>
</Tooltip>
<WatchlistAddModal { ...addModalProps } onClose={ handleAddModalClose } data={ formData } isAdd/>
<DeleteAddressModal { ...deleteModalProps } onClose={ handleDeleteModalClose } data={ formData }/>
</>
);
};
export default chakra(AddressFavoriteButton);
...@@ -107,7 +107,7 @@ const WatchList: React.FC = () => { ...@@ -107,7 +107,7 @@ const WatchList: React.FC = () => {
Add address Add address
</Button> </Button>
</Box> </Box>
<AddressModal { ...addressModalProps } onClose={ onAddressModalClose } data={ addressModalData }/> <AddressModal { ...addressModalProps } onClose={ onAddressModalClose } data={ addressModalData } isAdd={ !addressModalData }/>
{ deleteModalData && <DeleteAddressModal { ...deleteModalProps } onClose={ onDeleteModalClose } data={ deleteModalData }/> } { deleteModalData && <DeleteAddressModal { ...deleteModalProps } onClose={ onDeleteModalClose } data={ deleteModalData }/> }
</> </>
); );
......
import { Tooltip, IconButton, Icon, HStack } from '@chakra-ui/react'; import { Tooltip, IconButton, Icon, HStack } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React from 'react';
import DeleteIcon from 'icons/delete.svg'; import DeleteIcon from 'icons/delete.svg';
import EditIcon from 'icons/edit.svg'; import EditIcon from 'icons/edit.svg';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
type Props = { type Props = {
onEditClick: () => void; onEditClick: () => void;
...@@ -10,8 +11,7 @@ type Props = { ...@@ -10,8 +11,7 @@ type Props = {
} }
const TableItemActionButtons = ({ onEditClick, onDeleteClick }: Props) => { const TableItemActionButtons = ({ onEditClick, onDeleteClick }: Props) => {
// prevent set focus on button when closing modal const onFocusCapture = usePreventFocusAfterModalClosing();
const onFocusCapture = useCallback((e: React.SyntheticEvent) => e.stopPropagation(), []);
return ( return (
<HStack spacing={ 6 } alignSelf="flex-end"> <HStack spacing={ 6 } alignSelf="flex-end">
......
...@@ -29,9 +29,10 @@ const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const; ...@@ -29,9 +29,10 @@ const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const;
const TAG_MAX_LENGTH = 35; const TAG_MAX_LENGTH = 35;
type Props = { type Props = {
data?: TWatchlistItem; data?: Partial<TWatchlistItem>;
onClose: () => void; onClose: () => void;
setAlertVisible: (isAlertVisible: boolean) => void; setAlertVisible: (isAlertVisible: boolean) => void;
isAdd: boolean;
} }
type Inputs = { type Inputs = {
...@@ -62,12 +63,12 @@ type Checkboxes = 'notification' | ...@@ -62,12 +63,12 @@ type Checkboxes = 'notification' |
'notification_settings.ERC-721.outcoming' | 'notification_settings.ERC-721.outcoming' |
'notification_settings.ERC-721.incoming'; 'notification_settings.ERC-721.incoming';
const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible, isAdd }) => {
const [ pending, setPending ] = useState(false); const [ pending, setPending ] = useState(false);
const formBackgroundColor = useColorModeValue('white', 'gray.900'); const formBackgroundColor = useColorModeValue('white', 'gray.900');
let notificationsDefault = {} as Inputs['notification_settings']; let notificationsDefault = {} as Inputs['notification_settings'];
if (!data) { if (!data?.notification_settings) {
NOTIFICATIONS.forEach(n => notificationsDefault[n] = { incoming: true, outcoming: true }); NOTIFICATIONS.forEach(n => notificationsDefault[n] = { incoming: true, outcoming: true });
} else { } else {
notificationsDefault = data.notification_settings; notificationsDefault = data.notification_settings;
...@@ -77,7 +78,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -77,7 +78,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
defaultValues: { defaultValues: {
address: data?.address_hash || '', address: data?.address_hash || '',
tag: data?.name || '', tag: data?.name || '',
notification: data ? data.notification_methods.email : true, notification: data?.notification_methods ? data.notification_methods.email : true,
notification_settings: notificationsDefault, notification_settings: notificationsDefault,
}, },
mode: 'onTouched', mode: 'onTouched',
...@@ -95,7 +96,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -95,7 +96,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
email: formData.notification, email: formData.notification,
}, },
}; };
if (data) { if (!isAdd && data) {
// edit address // edit address
return fetch<TWatchlistItem, WatchlistErrors>(`/node-api/account/watchlist/${ data.id }`, { method: 'PUT', body }); return fetch<TWatchlistItem, WatchlistErrors>(`/node-api/account/watchlist/${ data.id }`, { method: 'PUT', body });
...@@ -193,7 +194,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -193,7 +194,7 @@ const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
isLoading={ pending } isLoading={ pending }
disabled={ !isValid || !isDirty } disabled={ !isValid || !isDirty }
> >
{ data ? 'Save changes' : 'Add address' } { !isAdd ? 'Save changes' : 'Add address' }
</Button> </Button>
</Box> </Box>
</form> </form>
......
...@@ -7,20 +7,21 @@ import FormModal from 'ui/shared/FormModal'; ...@@ -7,20 +7,21 @@ import FormModal from 'ui/shared/FormModal';
import AddressForm from './AddressForm'; import AddressForm from './AddressForm';
type Props = { type Props = {
isAdd: boolean;
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
data?: TWatchlistItem; data?: Partial<TWatchlistItem>;
} }
const AddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => { const AddressModal: React.FC<Props> = ({ isOpen, onClose, data, isAdd }) => {
const title = data ? 'Edit watch list address' : 'New address to watch list'; const title = !isAdd ? 'Edit watch list address' : 'New address to watch list';
const text = !data ? 'An email notification can be sent to you when an address on your watch list sends or receives any transactions.' : ''; const text = isAdd ? 'An email notification can be sent to you when an address on your watch list sends or receives any transactions.' : '';
const [ isAlertVisible, setAlertVisible ] = useState(false); const [ isAlertVisible, setAlertVisible ] = useState(false);
const renderForm = useCallback(() => { const renderForm = useCallback(() => {
return <AddressForm data={ data } onClose={ onClose } setAlertVisible={ setAlertVisible }/>; return <AddressForm data={ data } onClose={ onClose } setAlertVisible={ setAlertVisible } isAdd={ isAdd }/>;
}, [ data, onClose ]); }, [ data, isAdd, onClose ]);
return ( return (
<FormModal<TWatchlistItem> <FormModal<TWatchlistItem>
......
...@@ -12,7 +12,7 @@ import DeleteModal from 'ui/shared/DeleteModal'; ...@@ -12,7 +12,7 @@ import DeleteModal from 'ui/shared/DeleteModal';
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
data: TWatchlistItem; data: Pick<TWatchlistItem, 'address_hash' | 'id'>;
} }
const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => { const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment