Commit f884b1f5 authored by tom's avatar tom

skeletons for tokens page

parent 0bc65c00
......@@ -383,7 +383,7 @@ export const RESOURCES = {
},
tokens: {
path: '/api/v2/tokens',
paginationFields: [ 'holder_count' as const, 'items_count' as const, 'name' as const ],
paginationFields: [ 'holder_count' as const, 'items_count' as const, 'name' as const, 'market_cap' as const ],
filterFields: [ 'q' as const, 'type' as const ],
},
......
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 Tokens from 'ui/pages/Tokens';
import Page from 'ui/shared/Page/Page';
const Tokens = dynamic(() => import('ui/pages/Tokens'), { ssr: false });
const TokensPage: NextPage = () => {
const title = getNetworkTitle();
......@@ -12,7 +15,9 @@ const TokensPage: NextPage = () => {
<Head>
<title>{ title }</title>
</Head>
<Tokens/>
<Page>
<Tokens/>
</Page>
</>
);
};
......
......@@ -9,11 +9,11 @@ import { generateListStub } from './utils';
export const TOKEN_INFO_ERC_20: TokenInfo<'ERC-20'> = {
address: ADDRESS_HASH,
decimals: '18',
exchange_rate: null,
exchange_rate: '0.999997',
holders: '16026',
name: 'Stub Token (goerli)',
symbol: 'STUB',
total_supply: '6000000000000000000',
total_supply: '60000000000000000000000',
type: 'ERC-20',
icon_url: null,
};
......
......@@ -7,6 +7,7 @@ export type TokensResponse = {
holder_count: number;
items_count: number;
name: string;
market_cap: string | null;
};
}
......
import React from 'react';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import TokensList from 'ui/tokens/Tokens';
const Tokens = () => {
return (
<Page>
<>
<PageTitle title="Tokens" withTextAd/>
<TokensList/>
</Page>
</>
);
};
......
import { Box, chakra, Icon, Tooltip } from '@chakra-ui/react';
import { Box, chakra, Icon, Skeleton, Tooltip } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
......@@ -11,9 +11,10 @@ import { WALLETS_INFO } from 'lib/web3/wallets';
interface Props {
className?: string;
token: TokenInfo;
isLoading?: boolean;
}
const AddressAddToWallet = ({ className, token }: Props) => {
const AddressAddToWallet = ({ className, token, isLoading }: Props) => {
const toast = useToast();
const provider = useProvider();
......@@ -59,6 +60,10 @@ const AddressAddToWallet = ({ className, token }: Props) => {
return null;
}
if (isLoading) {
return <Skeleton className={ className } boxSize={ 6 } borderRadius="base"/>;
}
const defaultWallet = appConfig.web3.defaultWallet;
return (
......
......@@ -9,6 +9,8 @@ import useDebounce from 'lib/hooks/useDebounce';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { apos } from 'lib/html-entities';
import TOKEN_TYPE from 'lib/token/tokenTypes';
import { TOKEN_INFO_ERC_20 } from 'stubs/token';
import { generateListStub } from 'stubs/utils';
import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DataListDisplay from 'ui/shared/DataListDisplay';
......@@ -25,14 +27,22 @@ const getTokenFilterValue = (getFilterValuesFromQuery<TokenType>).bind(null, TOK
const Tokens = () => {
const router = useRouter();
const [ filter, setFilter ] = React.useState<string>(router.query.filter?.toString() || '');
const [ filter, setFilter ] = React.useState<string>(router.query.q?.toString() || '');
const [ type, setType ] = React.useState<Array<TokenType> | undefined>(getTokenFilterValue(router.query.type));
const debouncedFilter = useDebounce(filter, 300);
const { isError, isLoading, data, isPaginationVisible, pagination, onFilterChange } = useQueryWithPages({
const { isError, isPlaceholderData, data, isPaginationVisible, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'tokens',
filters: { q: debouncedFilter, type },
options: {
placeholderData: generateListStub<'tokens'>(TOKEN_INFO_ERC_20, 50, {
holder_count: 81528,
items_count: 50,
name: '',
market_cap: null,
}),
},
});
const onSearchChange = useCallback((value: string) => {
......@@ -84,15 +94,23 @@ const Tokens = () => {
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
{ data.items.map((item, index) => <TokensListItem key={ item.address } token={ item } index={ index } page={ pagination.page }/>) }
{ data.items.map((item, index) => (
<TokensListItem
key={ item.address + (isPlaceholderData ? index : '') }
token={ item }
index={ index }
page={ pagination.page }
isLoading={ isPlaceholderData }
/>
)) }
</Show>
<Hide below="lg" ssr={ false }><TokensTable items={ data.items } page={ pagination.page }/></Hide></>
<Hide below="lg" ssr={ false }><TokensTable items={ data.items } page={ pagination.page } isLoading={ isPlaceholderData }/></Hide></>
) : null;
return (
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
isLoading={ false }
items={ data?.items }
skeletonProps={{ skeletonDesktopColumns: [ '25px', '33%', '33%', '33%', '110px' ] }}
emptyText="There are no tokens."
......
import { Flex, Text, Tag, HStack, Grid, GridItem } from '@chakra-ui/react';
import { Flex, HStack, Grid, GridItem, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
......@@ -6,6 +6,7 @@ import type { TokenInfo } from 'types/api/token';
import getCurrencyValue from 'lib/getCurrencyValue';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenLogo from 'ui/shared/TokenLogo';
......@@ -14,6 +15,7 @@ type Props = {
token: TokenInfo;
index: number;
page: number;
isLoading?: boolean;
}
const PAGE_SIZE = 50;
......@@ -22,6 +24,7 @@ const TokensTableItem = ({
token,
page,
index,
isLoading,
}: Props) => {
const {
......@@ -47,37 +50,39 @@ const TokensTableItem = ({
>
<GridItem display="flex">
<Flex overflow="hidden" mr={ 3 } alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 }/>
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString }/>
<Tag flexShrink={ 0 } ml={ 3 }>{ type }</Tag>
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading } mr={ 3 }/>
<Tag flexShrink={ 0 } isLoading={ isLoading }>{ type }</Tag>
</Flex>
<Text fontSize="sm" ml="auto" variant="secondary">{ (page - 1) * PAGE_SIZE + index + 1 }</Text>
<Skeleton isLoaded={ !isLoading } fontSize="sm" ml="auto" color="text_secondary" minW="24px" textAlign="right" lineHeight={ 6 }>
<span>{ (page - 1) * PAGE_SIZE + index + 1 }</span>
</Skeleton>
</GridItem>
</Grid>
<Flex justifyContent="space-between" alignItems="center" width="100%">
<Flex alignItems="center" width="136px" justifyContent="space-between" ml={ 8 } mt="-8px">
<Flex alignItems="center">
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant"/>
<CopyToClipboard text={ address } ml={ 1 }/>
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant" isLoading={ isLoading }/>
<CopyToClipboard text={ address } isLoading={ isLoading }/>
</Flex>
<AddressAddToWallet token={ token }/>
<AddressAddToWallet token={ token } isLoading={ isLoading }/>
</Flex>
</Flex>
{ exchangeRate && (
<HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Price</Text>
<Text fontSize="sm" variant="secondary">{ exchangeRate }</Text>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Price</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary"><span>{ exchangeRate }</span></Skeleton>
</HStack>
) }
{ totalValue?.usd && (
<HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>On-chain market cap</Text>
<Text fontSize="sm" variant="secondary">{ totalValue.usd }</Text>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>On-chain market cap</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" variant="secondary"><span>{ totalValue.usd }</span></Skeleton>
</HStack>
) }
<HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Holders</Text>
<Text fontSize="sm" variant="secondary">{ Number(holders).toLocaleString() }</Text>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Holders</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" variant="secondary"><span>{ Number(holders).toLocaleString() }</span></Skeleton>
</HStack>
</ListItemMobile>
);
......
......@@ -10,9 +10,10 @@ import TokensTableItem from './TokensTableItem';
type Props = {
items: Array<TokenInfo>;
page: number;
isLoading?: boolean;
}
const TokensTable = ({ items, page }: Props) => {
const TokensTable = ({ items, page, isLoading }: Props) => {
return (
<Table style={{ tableLayout: 'auto' }}>
<Thead top={ 80 }>
......@@ -25,7 +26,7 @@ const TokensTable = ({ items, page }: Props) => {
</Thead>
<Tbody>
{ items.map((item, index) => (
<TokensTableItem key={ item.address } token={ item } index={ index } page={ page }/>
<TokensTableItem key={ item.address + (isLoading ? index : '') } token={ item } index={ index } page={ page } isLoading={ isLoading }/>
)) }
</Tbody>
</Table>
......
import { Box, Flex, Td, Tr, Text, Tag } from '@chakra-ui/react';
import { Box, Flex, Td, Tr, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import getCurrencyValue from 'lib/getCurrencyValue';
import Address from 'ui/shared/address/Address';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import TokenLogo from 'ui/shared/TokenLogo';
......@@ -13,6 +15,7 @@ type Props = {
token: TokenInfo;
index: number;
page: number;
isLoading?: boolean;
}
const PAGE_SIZE = 50;
......@@ -21,6 +24,7 @@ const TokensTableItem = ({
token,
page,
index,
isLoading,
}: Props) => {
const {
......@@ -41,8 +45,9 @@ const TokensTableItem = ({
return (
<Tr>
<Td>
<Flex>
<Text
<Flex alignItems="flex-start">
<Skeleton
isLoaded={ !isLoading }
fontSize="sm"
lineHeight="24px"
fontWeight={ 600 }
......@@ -50,28 +55,46 @@ const TokensTableItem = ({
minW="28px"
>
{ (page - 1) * PAGE_SIZE + index + 1 }
</Text>
</Skeleton>
<Box>
<Flex alignItems="center">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 }/>
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString }/>
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/>
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading }/>
</Flex>
<Flex alignItems="center" width="136px" justifyContent="space-between" ml={ 8 } mt={ 2 }>
<Flex alignItems="center">
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant" fontWeight={ 500 }/>
<CopyToClipboard text={ address } ml={ 1 }/>
</Flex>
<AddressAddToWallet token={ token }/>
</Flex>
<Tag flexShrink={ 0 } ml={ 8 } mt={ 3 }>{ type }</Tag>
<Box ml={ 8 } mt={ 2 }>
<Address>
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant" fontWeight={ 500 } isLoading={ isLoading }/>
<CopyToClipboard text={ address } isLoading={ isLoading }/>
<AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/>
</Address>
<Box mt={ 3 } >
<Tag isLoading={ isLoading }>{ type }</Tag>
</Box>
</Box>
</Box>
</Flex>
</Td>
<Td isNumeric><Text fontSize="sm" lineHeight="24px" fontWeight={ 500 }>{ exchangeRate && `$${ exchangeRate }` }</Text></Td>
<Td isNumeric>
<Skeleton isLoaded={ !isLoading } fontSize="sm" lineHeight="24px" fontWeight={ 500 } display="inline-block">
{ exchangeRate && `$${ exchangeRate }` }
</Skeleton>
</Td>
<Td isNumeric maxWidth="300px" width="300px">
<Text fontSize="sm" lineHeight="24px" fontWeight={ 500 }>{ totalValue?.usd && `$${ totalValue.usd }` }</Text>
<Skeleton isLoaded={ !isLoading } fontSize="sm" lineHeight="24px" fontWeight={ 500 } display="inline-block">
{ totalValue?.usd && `$${ totalValue.usd }` }
</Skeleton>
</Td>
<Td isNumeric>
<Skeleton
isLoaded={ !isLoading }
fontSize="sm"
lineHeight="24px"
fontWeight={ 500 }
display="inline-block"
>
{ Number(holders).toLocaleString() }
</Skeleton>
</Td>
<Td isNumeric><Text fontSize="sm" lineHeight="24px" fontWeight={ 500 }>{ Number(holders).toLocaleString() }</Text></Td>
</Tr>
);
};
......
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