Commit 558f514c authored by tom's avatar tom

skeleton for token details

parent 842d9465
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react';
import type { PageParams } from 'lib/next/token/types';
import getSeo from 'lib/next/token/getSeo';
import Token from 'ui/pages/Token';
import Page from 'ui/shared/Page/Page';
const Token = dynamic(() => import('ui/pages/Token'), { ssr: false });
const TokenPage: NextPage<PageParams> = ({ hash }: PageParams) => {
const { title, description } = getSeo({ hash });
......@@ -16,7 +19,9 @@ const TokenPage: NextPage<PageParams> = ({ hash }: PageParams) => {
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Page>
<Token/>
</Page>
</>
);
};
......
import type { TokenInfo } from 'types/api/token';
import type { TokenCounters, TokenInfo } from 'types/api/token';
export const TOKEN_INFO: TokenInfo<'ERC-20'> = {
address: '0x2B51Ae4412F79c3c1cB12AA40Ea4ECEb4e80511a',
......@@ -10,3 +10,8 @@ export const TOKEN_INFO: TokenInfo<'ERC-20'> = {
total_supply: '6000000000000000',
type: 'ERC-20',
};
export const TOKEN_COUNTERS: TokenCounters = {
token_holders_count: '123456',
transfers_count: '123456',
};
......@@ -14,7 +14,6 @@ import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import * as stubs from 'stubs/token';
import AddressContract from 'ui/address/AddressContract';
import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import Pagination from 'ui/shared/Pagination';
......@@ -154,7 +153,7 @@ const TokenPageContent = () => {
}, [ isMobile ]);
return (
<Page>
<>
<TextAd mb={ 6 }/>
<PageTitle
isLoading={ tokenQuery.isPlaceholderData }
......@@ -181,7 +180,7 @@ const TokenPageContent = () => {
) }
{ !tokenQuery.isLoading && !tokenQuery.isError && <Box h={{ base: 0, lg: '40vh' }}/> }
</Page>
</>
);
};
......
import { GridItem, Flex, Text } from '@chakra-ui/react';
import { GridItem, Flex, Text, Skeleton } from '@chakra-ui/react';
import type { HTMLChakraProps } from '@chakra-ui/system';
import React from 'react';
......@@ -9,18 +9,21 @@ interface Props extends Omit<HTMLChakraProps<'div'>, 'title'> {
hint: string;
children: React.ReactNode;
note?: string;
isLoading?: boolean;
}
const DetailsInfoItem = ({ title, hint, note, children, id, ...styles }: Props) => {
const DetailsInfoItem = ({ title, hint, note, children, id, isLoading, ...styles }: Props) => {
return (
<>
<GridItem py={{ base: 1, lg: 2 }} id={ id } lineHeight={ 5 } { ...styles } whiteSpace="nowrap" _notFirst={{ mt: { base: 3, lg: 0 } }}>
<Flex columnGap={ 2 } alignItems="flex-start">
<Hint label={ hint }/>
<Hint label={ hint } isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading }>
<Text fontWeight={{ base: 700, lg: 500 }}>
{ title }
{ note && <Text fontWeight={ 500 } variant="secondary" fontSize="xs" className="note" align="right">{ note }</Text> }
</Text>
</Skeleton>
</Flex>
</GridItem>
<GridItem
......
import type { TooltipProps } from '@chakra-ui/react';
import { chakra, IconButton, Tooltip, useDisclosure } from '@chakra-ui/react';
import { chakra, IconButton, Tooltip, useDisclosure, Skeleton } from '@chakra-ui/react';
import React from 'react';
import InfoIcon from 'icons/info.svg';
......@@ -8,9 +8,10 @@ interface Props {
label: string | React.ReactNode;
className?: string;
tooltipProps?: Partial<TooltipProps>;
isLoading?: boolean;
}
const Hint = ({ label, className, tooltipProps }: Props) => {
const Hint = ({ label, className, tooltipProps, isLoading }: Props) => {
// have to implement controlled tooltip because of the issue - https://github.com/chakra-ui/chakra-ui/issues/7107
const { isOpen, onOpen, onToggle, onClose } = useDisclosure();
......@@ -19,6 +20,10 @@ const Hint = ({ label, className, tooltipProps }: Props) => {
onToggle();
}, [ onToggle ]);
if (isLoading) {
return <Skeleton boxSize={ 5 } borderRadius="sm"/>;
}
return (
<Tooltip
label={ label }
......
......@@ -8,11 +8,11 @@ import type { TokenInfo } from 'types/api/token';
import useApiQuery from 'lib/api/useApiQuery';
import getCurrencyValue from 'lib/getCurrencyValue';
import { TOKEN_COUNTERS } from 'stubs/token';
import type { TokenTabs } from 'ui/pages/Token';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import DetailsSkeletonRow from 'ui/shared/skeletons/DetailsSkeletonRow';
interface Props {
tokenQuery: UseQueryResult<TokenInfo>;
......@@ -23,7 +23,7 @@ const TokenDetails = ({ tokenQuery }: Props) => {
const tokenCountersQuery = useApiQuery('token_counters', {
pathParams: { hash: router.query.hash?.toString() },
queryOptions: { enabled: Boolean(router.query.hash) },
queryOptions: { enabled: Boolean(router.query.hash), placeholderData: TOKEN_COUNTERS },
});
const changeUrlAndScroll = useCallback((tab: TokenTabs) => () => {
......@@ -56,32 +56,19 @@ const TokenDetails = ({ tokenQuery }: Props) => {
throw Error('Token fetch error', { cause: tokenQuery.error as unknown as Error });
}
if (tokenQuery.isLoading) {
return (
<Grid mt={ 10 } columnGap={ 8 } rowGap={{ base: 5, lg: 7 }} templateColumns={{ base: '1fr', lg: '210px 1fr' }} maxW="1000px">
<DetailsSkeletonRow w="10%"/>
<DetailsSkeletonRow w="30%"/>
<DetailsSkeletonRow w="30%"/>
<DetailsSkeletonRow w="20%"/>
<DetailsSkeletonRow w="20%"/>
<DetailsSkeletonRow w="10%"/>
</Grid>
);
}
const {
exchange_rate: exchangeRate,
total_supply: totalSupply,
decimals,
symbol,
type,
} = tokenQuery.data;
} = tokenQuery.data || {};
let marketcap;
let totalSupplyValue;
if (type === 'ERC-20') {
const totalValue = totalSupply !== null ? getCurrencyValue({ value: totalSupply, accuracy: 3, accuracyUsd: 2, exchangeRate, decimals }) : undefined;
const totalValue = totalSupply ? getCurrencyValue({ value: totalSupply, accuracy: 3, accuracyUsd: 2, exchangeRate, decimals }) : undefined;
marketcap = totalValue?.usd;
totalSupplyValue = totalValue?.valueStr;
} else {
......@@ -119,37 +106,47 @@ const TokenDetails = ({ tokenQuery }: Props) => {
alignSelf="center"
wordBreak="break-word"
whiteSpace="pre-wrap"
isLoading={ tokenQuery.isPlaceholderData }
>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData }>
<Flex w="100%">
<Box whiteSpace="nowrap" overflow="hidden">
<HashStringShortenDynamic hash={ totalSupplyValue || '0' }/>
</Box>
<Box flexShrink={ 0 }> { symbol || '' }</Box>
</Flex>
</Skeleton>
</DetailsInfoItem>
<DetailsInfoItem
title="Holders"
hint="Number of accounts holding the token"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
{ tokenCountersQuery.isLoading && <Skeleton w={ 20 } h={ 4 }/> }
{ !tokenCountersQuery.isLoading && countersItem('token_holders_count') }
<Skeleton isLoaded={ !tokenCountersQuery.isPlaceholderData }>
{ countersItem('token_holders_count') }
</Skeleton>
</DetailsInfoItem>
<DetailsInfoItem
title="Transfers"
hint="Number of transfer for the token"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
{ tokenCountersQuery.isLoading && <Skeleton w={ 20 } h={ 4 }/> }
{ !tokenCountersQuery.isLoading && countersItem('transfers_count') }
<Skeleton isLoaded={ !tokenCountersQuery.isPlaceholderData }>
{ countersItem('transfers_count') }
</Skeleton>
</DetailsInfoItem>
{ decimals && (
<DetailsInfoItem
title="Decimals"
hint="Number of digits that come after the decimal place when displaying token value"
alignSelf="center"
isLoading={ tokenQuery.isPlaceholderData }
>
<Skeleton isLoaded={ !tokenQuery.isPlaceholderData } minW={ 6 }>
{ decimals }
</Skeleton>
</DetailsInfoItem>
) }
<DetailsSponsoredItem/>
......
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