Commit 618d235d authored by tom's avatar tom

manage success and failed submit

parent bbe33680
......@@ -112,14 +112,14 @@ export const RESOURCES = {
basePath: appConfig.contractInfoApi.basePath,
},
token_info_application_config: {
token_info_applications_config: {
path: '/api/v1/chains/:chainId/token-info-submissions/selectors',
pathParams: [ 'chainId' as const ],
endpoint: appConfig.adminServiceApi.endpoint,
basePath: appConfig.adminServiceApi.basePath,
},
token_info_application: {
token_info_applications: {
path: '/api/v1/chains/:chainId/token-info-submissions',
pathParams: [ 'chainId' as const ],
endpoint: appConfig.adminServiceApi.endpoint,
......@@ -525,8 +525,8 @@ Q extends 'private_tags_tx' ? TransactionTags :
Q extends 'api_keys' ? ApiKeys :
Q extends 'watchlist' ? Array<WatchlistAddress> :
Q extends 'verified_addresses' ? VerifiedAddressResponse :
Q extends 'token_info_application_config' ? TokenInfoApplicationConfig :
Q extends 'token_info_application' ? TokenInfoApplications :
Q extends 'token_info_applications_config' ? TokenInfoApplicationConfig :
Q extends 'token_info_applications' ? TokenInfoApplications :
Q extends 'homepage_stats' ? HomeStats :
Q extends 'homepage_chart_txs' ? ChartTransactionResponse :
Q extends 'homepage_chart_market' ? ChartMarketResponse :
......
import { Icon, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Link } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { AddressVerificationFormFirstStepFields, AddressCheckStatusSuccess } from './types';
import type { VerifiedAddress, VerifiedAddressResponse } from 'types/api/account';
import type { VerifiedAddress } from 'types/api/account';
import appConfig from 'configs/app/config';
import eastArrowIcon from 'icons/arrows/east.svg';
import { getResourceKey } from 'lib/api/useApiQuery';
import Web3ModalProvider from 'ui/shared/Web3ModalProvider';
import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress';
......@@ -17,33 +14,22 @@ import AddressVerificationStepSuccess from './steps/AddressVerificationStepSucce
interface Props {
isOpen: boolean;
onClose: () => void;
onSubmit: (address: VerifiedAddress) => void;
}
const AddressVerificationModal = ({ isOpen, onClose }: Props) => {
const AddressVerificationModal = ({ isOpen, onClose, onSubmit }: Props) => {
const [ stepIndex, setStepIndex ] = React.useState(0);
const [ data, setData ] = React.useState<AddressVerificationFormFirstStepFields & AddressCheckStatusSuccess>({ address: '', signingMessage: '' });
const queryClient = useQueryClient();
const handleGoToSecondStep = React.useCallback((firstStepResult: typeof data) => {
setData(firstStepResult);
setStepIndex((prev) => prev + 1);
}, []);
const handleGoToThirdStep = React.useCallback((newItem: VerifiedAddress) => {
queryClient.setQueryData(
getResourceKey('verified_addresses', { pathParams: { chainId: appConfig.network.id } }),
(prevData: VerifiedAddressResponse | undefined) => {
if (!prevData) {
return { verifiedAddresses: [ newItem ] };
}
return {
verifiedAddresses: [ newItem, ...prevData.verifiedAddresses ],
};
});
const handleGoToThirdStep = React.useCallback((address: VerifiedAddress) => {
onSubmit(address);
setStepIndex((prev) => prev + 1);
}, [ queryClient ]);
}, [ onSubmit ]);
const handleGoToPrevStep = React.useCallback(() => {
setStepIndex((prev) => prev - 1);
......
import { OrderedList, ListItem, chakra, Button, useDisclosure, Show, Hide, Skeleton, Box } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { VerifiedAddress, TokenInfoApplication, TokenInfoApplications, VerifiedAddressResponse } from 'types/api/account';
import appConfig from 'configs/app/config';
import useApiQuery from 'lib/api/useApiQuery';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
import AccountPageDescription from 'ui/shared/AccountPageDescription';
......@@ -24,9 +27,10 @@ const VerifiedAddresses = () => {
const addressesQuery = useApiQuery('verified_addresses', {
pathParams: { chainId: appConfig.network.id },
});
const applicationsQuery = useApiQuery('token_info_application', {
const applicationsQuery = useApiQuery('token_info_applications', {
pathParams: { chainId: appConfig.network.id },
});
const queryClient = useQueryClient();
const handleGoBack = React.useCallback(() => {
setSelectedAddress(undefined);
......@@ -39,6 +43,33 @@ const VerifiedAddresses = () => {
setSelectedAddress(address);
}, []);
const handleAddressSubmit = React.useCallback((newItem: VerifiedAddress) => {
queryClient.setQueryData(
getResourceKey('verified_addresses', { pathParams: { chainId: appConfig.network.id } }),
(prevData: VerifiedAddressResponse | undefined) => {
if (!prevData) {
return { verifiedAddresses: [ newItem ] };
}
return {
verifiedAddresses: [ newItem, ...prevData.verifiedAddresses ],
};
});
}, [ queryClient ]);
const handleApplicationSubmit = React.useCallback((newItem: TokenInfoApplication) => {
queryClient.setQueryData(
getResourceKey('token_info_applications', { pathParams: { chainId: appConfig.network.id } }),
(prevData: TokenInfoApplications | undefined) => {
if (!prevData) {
return { submissions: [ newItem ] };
}
const submissions = prevData.submissions.map((item) => item.id === newItem.id ? newItem : item);
return { submissions };
});
}, [ queryClient ]);
const addButton = (
<Box marginTop={ 8 }>
<Button size="lg" onClick={ modalProps.onOpen }>
......@@ -78,6 +109,7 @@ const VerifiedAddresses = () => {
<TokenInfoForm
address={ selectedAddress }
application={ applicationsQuery.data?.submissions.find(({ tokenAddress }) => tokenAddress === selectedAddress) }
onSubmit={ handleApplicationSubmit }
/>
</Page>
);
......@@ -136,7 +168,7 @@ const VerifiedAddresses = () => {
skeletonProps={{ customSkeleton: skeleton }}
/>
{ addButton }
<AddressVerificationModal isOpen={ modalProps.isOpen } onClose={ modalProps.onClose }/>
<AddressVerificationModal isOpen={ modalProps.isOpen } onClose={ modalProps.onClose } onSubmit={ handleAddressSubmit }/>
</Page>
);
};
......
import { Button, Grid, GridItem } from '@chakra-ui/react';
import { Alert, Button, Grid, GridItem } from '@chakra-ui/react';
import React from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
......@@ -9,6 +9,7 @@ import type { TokenInfoApplication } from 'types/api/account';
import appConfig from 'configs/app/config';
import useApiFetch from 'lib/api/useApiFetch';
import useApiQuery from 'lib/api/useApiQuery';
import useToast from 'lib/hooks/useToast';
import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
......@@ -31,13 +32,15 @@ import { getFormDefaultValues, prepareRequestBody } from './utils';
interface Props {
address: string;
application?: TokenInfoApplication;
onSubmit: (application: TokenInfoApplication) => void;
}
const TokenInfoForm = ({ address, application }: Props) => {
const TokenInfoForm = ({ address, application, onSubmit }: Props) => {
const apiFetch = useApiFetch();
const toast = useToast();
const configQuery = useApiQuery('token_info_application_config', {
const configQuery = useApiQuery('token_info_applications_config', {
pathParams: { chainId: appConfig.network.id },
});
......@@ -50,17 +53,30 @@ const TokenInfoForm = ({ address, application }: Props) => {
const onFormSubmit: SubmitHandler<Fields> = React.useCallback(async(data) => {
try {
const submission = prepareRequestBody(data);
await apiFetch('token_info_application', {
const result = await apiFetch<'token_info_applications', TokenInfoApplication, { message: string }>('token_info_applications', {
pathParams: { chainId: appConfig.network.id },
fetchParams: {
method: 'POST',
body: { submission },
},
});
} catch (error) {}
}, [ apiFetch ]);
const onSubmit = handleSubmit(onFormSubmit);
if ('id' in result) {
onSubmit(result);
} else {
throw result;
}
} catch (error) {
toast({
position: 'top-right',
title: 'Error',
description: (error as Error)?.message || 'Something went wrong. Try again later.',
status: 'error',
variant: 'subtle',
isClosable: true,
});
}
}, [ apiFetch, onSubmit, toast ]);
if (configQuery.isError) {
return <DataFetchAlert/>;
......@@ -73,8 +89,10 @@ const TokenInfoForm = ({ address, application }: Props) => {
const fieldProps = { control, isReadOnly: application?.status === 'IN_PROCESS' };
return (
<form noValidate onSubmit={ onSubmit } autoComplete="off">
<form noValidate onSubmit={ handleSubmit(onFormSubmit) } autoComplete="off">
<div>Requests are sent to a moderator for review and approval. This process can take several days.</div>
{ application?.status === 'IN_PROCESS' &&
<Alert status="warning" mt={ 6 }>Request in progress. Once an admin approves your request you can edit token info.</Alert> }
<Grid mt={ 8 } gridTemplateColumns={{ base: '1fr', lg: '1fr 1fr' }} columnGap={ 5 } rowGap={ 5 }>
<GridItem colSpan={{ base: 1, lg: 2 }}>
......
......@@ -19,9 +19,6 @@ interface Props {
const TokenInfoFieldIconUrl = ({ control, isReadOnly, trigger }: Props) => {
const [ valueForPreview, setValueForPreview ] = React.useState<string>();
const imageLoadError = React.useRef(false);
const validatePreview = React.useCallback(() => {
return imageLoadError.current ? 'Unable to load image' : true;
}, [ ]);
......@@ -35,6 +32,9 @@ const TokenInfoFieldIconUrl = ({ control, isReadOnly, trigger }: Props) => {
},
});
const [ valueForPreview, setValueForPreview ] = React.useState<string>(field.value);
const imageLoadError = React.useRef(false);
const handleImageLoadSuccess = React.useCallback(() => {
imageLoadError.current = false;
trigger('icon_url');
......@@ -46,7 +46,6 @@ const TokenInfoFieldIconUrl = ({ control, isReadOnly, trigger }: Props) => {
}, [ trigger ]);
const handleBlur = React.useCallback(() => {
// make trigger()
field.onBlur();
const isValidUrl = validateUrl(field.value);
isValidUrl === true && setValueForPreview(field.value);
......
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