Files
explore/ui/watchlist/AddressModal/AddressForm.tsx
T
5ddd723bd8 Update routing (#265)
* update routing

* debug routing

* debug profile page

* add app protocol

* prefix nextjs api path

* remove debug

* debug routing

* debug deploy

* debug routing

* fix after merge

* delete port and protocol

* hard reload page for nav links

* fix logo

* add marketplace to sokol

* debug values for review

* add login link

* seamless page transition inside new ui app

* logout link

* debug values for review

* add redirect from profile to auth

* static assets

* debug review workflow

* update values for all envs

Co-authored-by: Yuliya Sharafitdinova <[email protected]>
Co-authored-by: tom <[email protected]>
2022-10-27 21:50:58 +03:00

204 lines
6.2 KiB
TypeScript

import {
Box,
Button,
Text,
useColorModeValue,
} from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react';
import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form';
import type { WatchlistErrors } from 'types/api/account';
import type { TWatchlistItem } from 'types/client/account';
import { QueryKeys } from 'types/client/accountQueries';
import getErrorMessage from 'lib/getErrorMessage';
import type { ErrorType } from 'lib/hooks/useFetch';
import useFetch from 'lib/hooks/useFetch';
import { ADDRESS_REGEXP } from 'lib/validations/address';
import AddressInput from 'ui/shared/AddressInput';
import CheckboxInput from 'ui/shared/CheckboxInput';
import TagInput from 'ui/shared/TagInput';
import AddressFormNotifications from './AddressFormNotifications';
// does it depend on the network?
const NOTIFICATIONS = [ 'native', 'ERC-20', 'ERC-721' ] as const;
const TAG_MAX_LENGTH = 35;
type Props = {
data?: TWatchlistItem;
onClose: () => void;
setAlertVisible: (isAlertVisible: boolean) => void;
}
type Inputs = {
address: string;
tag: string;
notification: boolean;
notification_settings: {
'native': {
outcoming: boolean;
incoming: boolean;
};
'ERC-721': {
outcoming: boolean;
incoming: boolean;
};
'ERC-20': {
outcoming: boolean;
incoming: boolean;
};
};
}
type Checkboxes = 'notification' |
'notification_settings.native.outcoming' |
'notification_settings.native.incoming' |
'notification_settings.ERC-20.outcoming' |
'notification_settings.ERC-20.incoming' |
'notification_settings.ERC-721.outcoming' |
'notification_settings.ERC-721.incoming';
const AddressForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
const [ pending, setPending ] = useState(false);
const formBackgroundColor = useColorModeValue('white', 'gray.900');
let notificationsDefault = {} as Inputs['notification_settings'];
if (!data) {
NOTIFICATIONS.forEach(n => notificationsDefault[n] = { incoming: true, outcoming: true });
} else {
notificationsDefault = data.notification_settings;
}
const { control, handleSubmit, formState: { errors, isValid }, setError } = useForm<Inputs>({
defaultValues: {
address: data?.address_hash || '',
tag: data?.name || '',
notification: data ? data.notification_methods.email : true,
notification_settings: notificationsDefault,
},
mode: 'all',
});
const queryClient = useQueryClient();
const fetch = useFetch();
function updateWatchlist(formData: Inputs) {
const body = {
name: formData?.tag,
address_hash: formData?.address,
notification_settings: formData.notification_settings,
notification_methods: {
email: formData.notification,
},
};
if (data) {
// edit address
return fetch<TWatchlistItem, WatchlistErrors>(`/node-api/account/watchlist/${ data.id }`, { method: 'PUT', body });
} else {
// add address
return fetch<TWatchlistItem, WatchlistErrors>('/node-api/account/watchlist', { method: 'POST', body });
}
}
const { mutate } = useMutation(updateWatchlist, {
onSuccess: () => {
queryClient.refetchQueries([ QueryKeys.watchlist ]).then(() => {
onClose();
setPending(false);
});
},
onError: (e: ErrorType<WatchlistErrors>) => {
setPending(false);
if (e?.error?.address_hash || e?.error?.name) {
e?.error?.address_hash && setError('address', { type: 'custom', message: getErrorMessage(e.error, 'address_hash') });
e?.error?.name && setError('tag', { type: 'custom', message: getErrorMessage(e.error, 'name') });
} else if (e?.error?.watchlist_id) {
setError('address', { type: 'custom', message: getErrorMessage(e.error, 'watchlist_id') });
} else {
setAlertVisible(true);
}
},
});
const onSubmit: SubmitHandler<Inputs> = (formData) => {
setAlertVisible(false);
setPending(true);
mutate(formData);
};
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => {
return (
<AddressInput<Inputs, 'address'>
field={ field }
backgroundColor={ formBackgroundColor }
error={ errors.address }
/>
);
}, [ errors, formBackgroundColor ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput<Inputs, 'tag'> field={ field } error={ errors.tag } backgroundColor={ formBackgroundColor }/>;
}, [ errors, formBackgroundColor ]);
// eslint-disable-next-line react/display-name
const renderCheckbox = useCallback((text: string) => ({ field }: {field: ControllerRenderProps<Inputs, Checkboxes>}) => (
<CheckboxInput<Inputs, Checkboxes> text={ text } field={ field }/>
), []);
return (
<>
<Box marginBottom={ 5 }>
<Controller
name="address"
control={ control }
rules={{
pattern: ADDRESS_REGEXP,
required: true,
}}
render={ renderAddressInput }
/>
</Box>
<Box marginBottom={ 8 }>
<Controller
name="tag"
control={ control }
rules={{
maxLength: TAG_MAX_LENGTH,
required: true,
}}
render={ renderTagInput }
/>
</Box>
<Text variant="secondary" fontSize="sm" marginBottom={ 5 }>
Please select what types of notifications you will receive
</Text>
<Box marginBottom={ 8 }>
<AddressFormNotifications control={ control }/>
</Box>
<Text variant="secondary" fontSize="sm" marginBottom={{ base: '10px', lg: 5 }}>Notification methods</Text>
<Controller
name={ 'notification' as Checkboxes }
control={ control }
render={ renderCheckbox('Email notifications') }
/>
<Box marginTop={ 8 }>
<Button
size="lg"
onClick={ handleSubmit(onSubmit) }
isLoading={ pending }
disabled={ !isValid }
>
{ data ? 'Save changes' : 'Add address' }
</Button>
</Box>
</>
);
};
export default AddressForm;