mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
skeletons for verified addresses
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import type { NextPage } from 'next';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
|
||||
import getNetworkTitle from 'lib/networks/getNetworkTitle';
|
||||
import VerifiedAddresses from 'ui/pages/VerifiedAddresses';
|
||||
import Page from 'ui/shared/Page/Page';
|
||||
|
||||
const VerifiedAddresses = dynamic(() => import('ui/pages/VerifiedAddresses'), { ssr: false });
|
||||
|
||||
const VerifiedAddressesPage: NextPage = () => {
|
||||
const title = getNetworkTitle();
|
||||
return (
|
||||
|
||||
+26
-1
@@ -1,4 +1,4 @@
|
||||
import type { PublicTag, AddressTag, TransactionTag, ApiKey, CustomAbi } from 'types/api/account';
|
||||
import type { PublicTag, AddressTag, TransactionTag, ApiKey, CustomAbi, VerifiedAddress, TokenInfoApplication } from 'types/api/account';
|
||||
import type { TWatchlistItem } from 'types/client/account';
|
||||
|
||||
import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams';
|
||||
@@ -79,3 +79,28 @@ export const CUSTOM_ABI: CustomAbi = {
|
||||
id: '1',
|
||||
name: 'placeholder',
|
||||
};
|
||||
|
||||
export const VERIFIED_ADDRESS: VerifiedAddress = {
|
||||
userId: '[email protected]',
|
||||
chainId: '5',
|
||||
contractAddress: ADDRESS_HASH,
|
||||
verifiedDate: '2022-11-11',
|
||||
metadata: {
|
||||
tokenName: 'Placeholder Token',
|
||||
tokenSymbol: 'PLC',
|
||||
},
|
||||
};
|
||||
|
||||
export const TOKEN_INFO_APPLICATION: TokenInfoApplication = {
|
||||
id: '1',
|
||||
tokenAddress: ADDRESS_HASH,
|
||||
status: 'IN_PROCESS',
|
||||
updatedAt: '2022-11-11 13:49:48.031453Z',
|
||||
requesterName: 'John Doe',
|
||||
requesterEmail: '[email protected]',
|
||||
projectWebsite: 'http://example.com',
|
||||
projectEmail: '[email protected]',
|
||||
iconUrl: 'https://example.com/100/100',
|
||||
projectDescription: 'Hello!',
|
||||
projectSector: 'DeFi',
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrderedList, ListItem, chakra, Button, useDisclosure, Show, Hide, Skeleton, Box, Link } from '@chakra-ui/react';
|
||||
import { OrderedList, ListItem, chakra, Button, useDisclosure, Show, Hide, Skeleton, Link } from '@chakra-ui/react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
@@ -9,12 +9,11 @@ import appConfig from 'configs/app/config';
|
||||
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
|
||||
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { TOKEN_INFO_APPLICATION, VERIFIED_ADDRESS } from 'stubs/account';
|
||||
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
|
||||
import AccountPageDescription from 'ui/shared/AccountPageDescription';
|
||||
import DataListDisplay from 'ui/shared/DataListDisplay';
|
||||
import PageTitle from 'ui/shared/Page/PageTitle';
|
||||
import SkeletonListAccount from 'ui/shared/skeletons/SkeletonListAccount';
|
||||
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
|
||||
import AdminSupportText from 'ui/shared/texts/AdminSupportText';
|
||||
import TokenInfoForm from 'ui/tokenInfo/TokenInfoForm';
|
||||
import VerifiedAddressesListItem from 'ui/verifiedAddresses/VerifiedAddressesListItem';
|
||||
@@ -35,12 +34,18 @@ const VerifiedAddresses = () => {
|
||||
}, [ ]);
|
||||
|
||||
const modalProps = useDisclosure();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const addressesQuery = useApiQuery('verified_addresses', {
|
||||
pathParams: { chainId: appConfig.network.id },
|
||||
queryOptions: {
|
||||
placeholderData: { verifiedAddresses: Array(3).fill(VERIFIED_ADDRESS) },
|
||||
},
|
||||
});
|
||||
const applicationsQuery = useApiQuery('token_info_applications', {
|
||||
pathParams: { chainId: appConfig.network.id, id: undefined },
|
||||
queryOptions: {
|
||||
placeholderData: { submissions: Array(3).fill(TOKEN_INFO_APPLICATION) },
|
||||
select: (data) => {
|
||||
return {
|
||||
...data,
|
||||
@@ -49,7 +54,8 @@ const VerifiedAddresses = () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const isLoading = addressesQuery.isPlaceholderData || applicationsQuery.isPlaceholderData;
|
||||
|
||||
const handleGoBack = React.useCallback(() => {
|
||||
setSelectedAddress(undefined);
|
||||
@@ -94,24 +100,11 @@ const VerifiedAddresses = () => {
|
||||
}, [ queryClient ]);
|
||||
|
||||
const addButton = (
|
||||
<Box marginTop={ 8 }>
|
||||
<Skeleton mt={ 8 } isLoaded={ !isLoading } display="inline-block">
|
||||
<Button size="lg" onClick={ modalProps.onOpen }>
|
||||
Add address
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const skeleton = (
|
||||
<>
|
||||
<Box display={{ base: 'block', lg: 'none' }}>
|
||||
<SkeletonListAccount/>
|
||||
<Skeleton height="44px" width="156px" marginTop={ 8 }/>
|
||||
</Box>
|
||||
<Box display={{ base: 'none', lg: 'block' }}>
|
||||
<SkeletonTable columns={ [ '100%', '180px', '260px', '160px' ] }/>
|
||||
<Skeleton height="44px" width="156px" marginTop={ 8 }/>
|
||||
</Box>
|
||||
</>
|
||||
</Skeleton>
|
||||
);
|
||||
|
||||
const backLink = React.useMemo(() => {
|
||||
@@ -144,13 +137,14 @@ const VerifiedAddresses = () => {
|
||||
const content = addressesQuery.data?.verifiedAddresses ? (
|
||||
<>
|
||||
<Show below="lg" key="content-mobile" ssr={ false }>
|
||||
{ addressesQuery.data.verifiedAddresses.map((item) => (
|
||||
{ addressesQuery.data.verifiedAddresses.map((item, index) => (
|
||||
<VerifiedAddressesListItem
|
||||
key={ item.contractAddress }
|
||||
key={ item.contractAddress + (isLoading ? index : '') }
|
||||
item={ item }
|
||||
application={ applicationsQuery.data?.submissions?.find(({ tokenAddress }) => tokenAddress.toLowerCase() === item.contractAddress.toLowerCase()) }
|
||||
onAdd={ handleItemAdd }
|
||||
onEdit={ handleItemEdit }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
)) }
|
||||
</Show>
|
||||
@@ -160,6 +154,7 @@ const VerifiedAddresses = () => {
|
||||
applications={ applicationsQuery.data?.submissions }
|
||||
onItemEdit={ handleItemEdit }
|
||||
onItemAdd={ handleItemAdd }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</Hide>
|
||||
</>
|
||||
@@ -192,12 +187,12 @@ const VerifiedAddresses = () => {
|
||||
<AdminSupportText mt={ 5 }/>
|
||||
</AccountPageDescription>
|
||||
<DataListDisplay
|
||||
isLoading={ addressesQuery.isLoading || applicationsQuery.isLoading }
|
||||
isLoading={ false }
|
||||
isError={ addressesQuery.isError || applicationsQuery.isError }
|
||||
items={ addressesQuery.data?.verifiedAddresses }
|
||||
content={ content }
|
||||
emptyText=""
|
||||
skeletonProps={{ customSkeleton: skeleton }}
|
||||
skeletonProps={{ customSkeleton: null }}
|
||||
/>
|
||||
{ addButton }
|
||||
<AddressVerificationModal
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Icon, IconButton, Link, Tooltip } from '@chakra-ui/react';
|
||||
import { IconButton, Link, Skeleton, Tooltip } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
|
||||
@@ -6,6 +6,7 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
|
||||
import editIcon from 'icons/edit.svg';
|
||||
import dayjs from 'lib/date/dayjs';
|
||||
import AddressSnippet from 'ui/shared/AddressSnippet';
|
||||
import Icon from 'ui/shared/chakra/Icon';
|
||||
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
|
||||
|
||||
import VerifiedAddressesStatus from './VerifiedAddressesStatus';
|
||||
@@ -16,64 +17,89 @@ interface Props {
|
||||
application: TokenInfoApplication | undefined;
|
||||
onAdd: (address: string) => void;
|
||||
onEdit: (address: string) => void;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit }: Props) => {
|
||||
const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading }: Props) => {
|
||||
const handleAddClick = React.useCallback(() => {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
onAdd(item.contractAddress);
|
||||
}, [ item, onAdd ]);
|
||||
}, [ isLoading, item.contractAddress, onAdd ]);
|
||||
|
||||
const handleEditClick = React.useCallback(() => {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
onEdit(item.contractAddress);
|
||||
}, [ item, onEdit ]);
|
||||
}, [ isLoading, item.contractAddress, onEdit ]);
|
||||
|
||||
const tokenInfo = (() => {
|
||||
if (isLoading) {
|
||||
return <Skeleton height={ 6 } width="140px"/>;
|
||||
}
|
||||
|
||||
if (!item.metadata.tokenName) {
|
||||
return <span>Not a token</span>;
|
||||
}
|
||||
|
||||
if (!application) {
|
||||
return <Link onClick={ handleAddClick }>Add details</Link>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<VerifiedAddressesTokenSnippet application={ application } name={ item.metadata.tokenName }/>
|
||||
<Tooltip label="Edit">
|
||||
<IconButton
|
||||
aria-label="edit"
|
||||
variant="simple"
|
||||
boxSize={ 5 }
|
||||
borderRadius="none"
|
||||
flexShrink={ 0 }
|
||||
onClick={ handleEditClick }
|
||||
icon={ <Icon as={ editIcon }/> }
|
||||
/>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
})();
|
||||
|
||||
return (
|
||||
<ListItemMobileGrid.Container>
|
||||
<ListItemMobileGrid.Label>Address</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value py="3px">
|
||||
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}/>
|
||||
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }} isLoading={ isLoading }/>
|
||||
</ListItemMobileGrid.Value>
|
||||
|
||||
{ item.metadata.tokenName && (
|
||||
<>
|
||||
<ListItemMobileGrid.Label>Token Info</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>Token Info</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value py={ application ? '3px' : '5px' } display="flex" alignItems="center">
|
||||
{ application ? (
|
||||
<>
|
||||
<VerifiedAddressesTokenSnippet application={ application } name={ item.metadata.tokenName }/>
|
||||
<Tooltip label="Edit">
|
||||
<IconButton
|
||||
aria-label="edit"
|
||||
variant="simple"
|
||||
boxSize={ 5 }
|
||||
borderRadius="none"
|
||||
flexShrink={ 0 }
|
||||
onClick={ handleEditClick }
|
||||
icon={ <Icon as={ editIcon }/> }
|
||||
/>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : (
|
||||
<Link onClick={ handleAddClick }>Add details</Link>
|
||||
) }
|
||||
{ tokenInfo }
|
||||
</ListItemMobileGrid.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ item.metadata.tokenName && application && (
|
||||
<>
|
||||
<ListItemMobileGrid.Label>Status</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>Status</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value>
|
||||
<VerifiedAddressesStatus status={ application.status }/>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block">
|
||||
<VerifiedAddressesStatus status={ application.status }/>
|
||||
</Skeleton>
|
||||
</ListItemMobileGrid.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
{ item.metadata.tokenName && application && (
|
||||
<>
|
||||
<ListItemMobileGrid.Label>Date</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Label isLoading={ isLoading }>Date</ListItemMobileGrid.Label>
|
||||
<ListItemMobileGrid.Value>
|
||||
{ dayjs(application.updatedAt).format('MMM DD, YYYY') }
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block">
|
||||
{ dayjs(application.updatedAt).format('MMM DD, YYYY') }
|
||||
</Skeleton>
|
||||
</ListItemMobileGrid.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
@@ -10,9 +10,10 @@ interface Props {
|
||||
applications: Array<TokenInfoApplication> | undefined;
|
||||
onItemAdd: (address: string) => void;
|
||||
onItemEdit: (address: string) => void;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd }: Props) => {
|
||||
const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd, isLoading }: Props) => {
|
||||
return (
|
||||
<Table variant="simple">
|
||||
<Thead>
|
||||
@@ -25,13 +26,14 @@ const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd }: P
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{ data.map((item) => (
|
||||
{ data.map((item, index) => (
|
||||
<VerifiedAddressesTableItem
|
||||
key={ item.contractAddress }
|
||||
key={ item.contractAddress + (isLoading ? index : '') }
|
||||
item={ item }
|
||||
application={ applications?.find(({ tokenAddress }) => tokenAddress.toLowerCase() === item.contractAddress.toLowerCase()) }
|
||||
onAdd={ onItemAdd }
|
||||
onEdit={ onItemEdit }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
)) }
|
||||
</Tbody>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Td, Tr, Link, Tooltip, IconButton, Icon } from '@chakra-ui/react';
|
||||
import { Td, Tr, Link, Tooltip, IconButton, Skeleton } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
|
||||
@@ -6,6 +6,7 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
|
||||
import editIcon from 'icons/edit.svg';
|
||||
import dayjs from 'lib/date/dayjs';
|
||||
import AddressSnippet from 'ui/shared/AddressSnippet';
|
||||
import Icon from 'ui/shared/chakra/Icon';
|
||||
|
||||
import VerifiedAddressesStatus from './VerifiedAddressesStatus';
|
||||
import VerifiedAddressesTokenSnippet from './VerifiedAddressesTokenSnippet';
|
||||
@@ -15,19 +16,30 @@ interface Props {
|
||||
application: TokenInfoApplication | undefined;
|
||||
onAdd: (address: string) => void;
|
||||
onEdit: (address: string) => void;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit }: Props) => {
|
||||
const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoading }: Props) => {
|
||||
|
||||
const handleAddClick = React.useCallback(() => {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
onAdd(item.contractAddress);
|
||||
}, [ item, onAdd ]);
|
||||
}, [ isLoading, item.contractAddress, onAdd ]);
|
||||
|
||||
const handleEditClick = React.useCallback(() => {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
onEdit(item.contractAddress);
|
||||
}, [ item, onEdit ]);
|
||||
}, [ isLoading, item.contractAddress, onEdit ]);
|
||||
|
||||
const tokenInfo = (() => {
|
||||
if (isLoading) {
|
||||
return <Skeleton height={ 6 } width="140px"/>;
|
||||
}
|
||||
|
||||
if (!item.metadata.tokenName) {
|
||||
return <span>Not a token</span>;
|
||||
}
|
||||
@@ -42,14 +54,14 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit }: Props)
|
||||
return (
|
||||
<Tr>
|
||||
<Td>
|
||||
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}/>
|
||||
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }} isLoading={ isLoading }/>
|
||||
</Td>
|
||||
<Td fontSize="sm" verticalAlign="middle" pr={ 1 }>
|
||||
{ tokenInfo }
|
||||
</Td>
|
||||
<Td pl="0">
|
||||
{ item.metadata.tokenName && application ? (
|
||||
<Tooltip label="Edit">
|
||||
{ item.metadata.tokenName && application && !isLoading ? (
|
||||
<Tooltip label={ isLoading ? undefined : 'Edit' }>
|
||||
<IconButton
|
||||
aria-label="edit"
|
||||
variant="simple"
|
||||
@@ -62,8 +74,16 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit }: Props)
|
||||
</Tooltip>
|
||||
) : null }
|
||||
</Td>
|
||||
<Td fontSize="sm"><VerifiedAddressesStatus status={ item.metadata.tokenName ? application?.status : undefined }/></Td>
|
||||
<Td fontSize="sm" color="text_secondary">{ item.metadata.tokenName && application ? dayjs(application.updatedAt).format('MMM DD, YYYY') : null }</Td>
|
||||
<Td fontSize="sm">
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block">
|
||||
<VerifiedAddressesStatus status={ item.metadata.tokenName ? application?.status : undefined }/>
|
||||
</Skeleton>
|
||||
</Td>
|
||||
<Td fontSize="sm" color="text_secondary">
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block">
|
||||
{ item.metadata.tokenName && application ? dayjs(application.updatedAt).format('MMM DD, YYYY') : null }
|
||||
</Skeleton>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user