Commit a6c5f0c4 authored by tom's avatar tom

skeleton for token inventory

parent 78c4fe54
import type { TokenCounters, TokenHolder, TokenInfo, TokenType } from 'types/api/token'; import type { TokenCounters, TokenHolder, TokenHolders, TokenInfo, TokenInstance, TokenInventoryResponse, TokenType } from 'types/api/token';
import type { TokenTransfer, TokenTransferResponse } from 'types/api/tokenTransfer'; import type { TokenTransfer, TokenTransferResponse } from 'types/api/tokenTransfer';
const ADDRESS_PARAMS = { const ADDRESS_PARAMS = {
...@@ -43,6 +43,8 @@ export const TOKEN_HOLDER: TokenHolder = { ...@@ -43,6 +43,8 @@ export const TOKEN_HOLDER: TokenHolder = {
value: '1021378038331138520668', value: '1021378038331138520668',
}; };
export const TOKEN_HOLDERS: TokenHolders = { items: Array(50).fill(TOKEN_HOLDER), next_page_params: null };
export const TOKEN_TRANSFER_ERC_20: TokenTransfer = { export const TOKEN_TRANSFER_ERC_20: TokenTransfer = {
block_hash: '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70', block_hash: '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70',
from: ADDRESS_PARAMS, from: ADDRESS_PARAMS,
...@@ -87,3 +89,26 @@ export const getTokenTransfersStub = (type?: TokenType): TokenTransferResponse = ...@@ -87,3 +89,26 @@ export const getTokenTransfersStub = (type?: TokenType): TokenTransferResponse =
return { items: Array(50).fill(TOKEN_TRANSFER_ERC_20), next_page_params: null }; return { items: Array(50).fill(TOKEN_TRANSFER_ERC_20), next_page_params: null };
} }
}; };
export const TOKEN_INSTANCE: TokenInstance = {
animation_url: null,
external_app_url: 'https://vipsland.com/nft/collections/genesis/188882',
id: '188882',
image_url: 'https://ipfs.vipsland.com/nft/collections/genesis/188882.gif',
is_unique: true,
metadata: {
attributes: Array(3).fill({ trait_type: 'skin', value: '6' }),
description: '**GENESIS #188882**, **8a77ca1bcaa4036f** :: *844th* generation of *#57806 and #57809* :: **eGenetic Hash Code (eDNA)** = *2822355e953a462d*',
external_url: 'https://vipsland.com/nft/collections/genesis/188882',
image: 'https://ipfs.vipsland.com/nft/collections/genesis/188882.gif',
name: 'GENESIS #188882, 8a77ca1bcaa4036f. Blockchain pixel PFP NFT + "on music video" trait inspired by God',
},
owner: ADDRESS_PARAMS,
token: TOKEN_INFO_ERC_1155,
holder_address_hash: '0x2B51Ae4412F79c3c1cB12AA40Ea4ECEb4e80511a',
};
export const TOKEN_INSTANCES: TokenInventoryResponse = {
items: Array(50).fill(TOKEN_INSTANCE),
next_page_params: null,
};
...@@ -53,7 +53,7 @@ export interface TokenInstanceTransfersCount { ...@@ -53,7 +53,7 @@ export interface TokenInstanceTransfersCount {
export interface TokenInventoryResponse { export interface TokenInventoryResponse {
items: Array<TokenInstance>; items: Array<TokenInstance>;
next_page_params: TokenInventoryPagination; next_page_params: TokenInventoryPagination | null;
} }
export type TokenInventoryPagination = { export type TokenInventoryPagination = {
......
...@@ -76,7 +76,7 @@ const TokenPageContent = () => { ...@@ -76,7 +76,7 @@ const TokenPageContent = () => {
scrollRef, scrollRef,
options: { options: {
enabled: Boolean(router.query.hash && router.query.tab === 'holders' && tokenQuery.data), enabled: Boolean(router.query.hash && router.query.tab === 'holders' && tokenQuery.data),
placeholderData: { items: Array(50).fill(stubs.TOKEN_HOLDER), next_page_params: null }, placeholderData: stubs.TOKEN_HOLDERS,
}, },
}); });
...@@ -86,6 +86,7 @@ const TokenPageContent = () => { ...@@ -86,6 +86,7 @@ const TokenPageContent = () => {
scrollRef, scrollRef,
options: { options: {
enabled: Boolean(router.query.hash && router.query.tab === 'inventory' && tokenQuery.data), enabled: Boolean(router.query.hash && router.query.tab === 'inventory' && tokenQuery.data),
placeholderData: stubs.TOKEN_INSTANCES,
}, },
}); });
......
...@@ -11,13 +11,14 @@ interface Props { ...@@ -11,13 +11,14 @@ interface Props {
imageUrl: string | null; imageUrl: string | null;
animationUrl: string | null; animationUrl: string | null;
className?: string; className?: string;
isLoading?: boolean;
} }
const NftMedia = ({ imageUrl, animationUrl, className }: Props) => { const NftMedia = ({ imageUrl, animationUrl, className, isLoading }: Props) => {
const [ type, setType ] = React.useState<MediaType | undefined>(!animationUrl ? 'image' : undefined); const [ type, setType ] = React.useState<MediaType | undefined>(!animationUrl ? 'image' : undefined);
React.useEffect(() => { React.useEffect(() => {
if (!animationUrl) { if (!animationUrl || isLoading) {
return; return;
} }
...@@ -45,9 +46,9 @@ const NftMedia = ({ imageUrl, animationUrl, className }: Props) => { ...@@ -45,9 +46,9 @@ const NftMedia = ({ imageUrl, animationUrl, className }: Props) => {
setType('image'); setType('image');
}); });
}, [ animationUrl ]); }, [ animationUrl, isLoading ]);
if (!type) { if (!type || isLoading) {
return ( return (
<AspectRatio <AspectRatio
className={ className } className={ className }
......
import { Grid, Skeleton } from '@chakra-ui/react'; import { Grid } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
...@@ -28,21 +28,6 @@ const TokenInventory = ({ inventoryQuery }: Props) => { ...@@ -28,21 +28,6 @@ const TokenInventory = ({ inventoryQuery }: Props) => {
</ActionBar> </ActionBar>
); );
const skeleton = (
<Grid
w="100%"
columnGap={{ base: 3, lg: 6 }}
rowGap={{ base: 3, lg: 6 }}
gridTemplateColumns={{ base: 'repeat(2, calc((100% - 12px)/2))', lg: 'repeat(auto-fill, minmax(210px, 1fr))' }}
>
<Skeleton w={{ base: '100%', lg: '210px' }} h="272px"/>
<Skeleton w={{ base: '100%', lg: '210px' }} h="272px"/>
<Skeleton w={{ base: '100%', lg: '210px' }} h="272px"/>
<Skeleton w={{ base: '100%', lg: '210px' }} h="272px"/>
<Skeleton w={{ base: '100%', lg: '210px' }} h="272px"/>
</Grid>
);
const items = inventoryQuery.data?.items; const items = inventoryQuery.data?.items;
const content = items ? ( const content = items ? (
...@@ -52,19 +37,25 @@ const TokenInventory = ({ inventoryQuery }: Props) => { ...@@ -52,19 +37,25 @@ const TokenInventory = ({ inventoryQuery }: Props) => {
rowGap={{ base: 3, lg: 6 }} rowGap={{ base: 3, lg: 6 }}
gridTemplateColumns={{ base: 'repeat(2, calc((100% - 12px)/2))', lg: 'repeat(auto-fill, minmax(210px, 1fr))' }} gridTemplateColumns={{ base: 'repeat(2, calc((100% - 12px)/2))', lg: 'repeat(auto-fill, minmax(210px, 1fr))' }}
> >
{ items.map((item) => <TokenInventoryItem key={ item.token.address + '_' + item.id } item={ item }/>) } { items.map((item, index) => (
<TokenInventoryItem
key={ item.token.address + '_' + item.id + (inventoryQuery.isPlaceholderData ? '_' + index : '') }
item={ item }
isLoading={ inventoryQuery.isPlaceholderData }
/>
)) }
</Grid> </Grid>
) : null; ) : null;
return ( return (
<DataListDisplay <DataListDisplay
isError={ inventoryQuery.isError } isError={ inventoryQuery.isError }
isLoading={ inventoryQuery.isLoading } isLoading={ false }
items={ items } items={ items }
emptyText="There are no tokens." emptyText="There are no tokens."
content={ content } content={ content }
actionBar={ actionBar } actionBar={ actionBar }
skeletonProps={{ customSkeleton: skeleton }} skeletonProps={{ customSkeleton: null }}
/> />
); );
}; };
......
import { Flex, Text, LinkBox, LinkOverlay, useColorModeValue, Hide } from '@chakra-ui/react'; import { Flex, Text, LinkBox, LinkOverlay, useColorModeValue, Hide, Skeleton } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import React from 'react'; import React from 'react';
...@@ -11,9 +11,9 @@ import LinkInternal from 'ui/shared/LinkInternal'; ...@@ -11,9 +11,9 @@ import LinkInternal from 'ui/shared/LinkInternal';
import NftMedia from 'ui/shared/nft/NftMedia'; import NftMedia from 'ui/shared/nft/NftMedia';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
type Props = { item: TokenInstance }; type Props = { item: TokenInstance; isLoading: boolean };
const NFTItem = ({ item }: Props) => { const NFTItem = ({ item, isLoading }: Props) => {
return ( return (
<LinkBox <LinkBox
w={{ base: '100%', lg: '210px' }} w={{ base: '100%', lg: '210px' }}
...@@ -32,6 +32,7 @@ const NFTItem = ({ item }: Props) => { ...@@ -32,6 +32,7 @@ const NFTItem = ({ item }: Props) => {
mb="18px" mb="18px"
imageUrl={ item.image_url } imageUrl={ item.image_url }
animationUrl={ item.animation_url } animationUrl={ item.animation_url }
isLoading={ isLoading }
/> />
</LinkOverlay> </LinkOverlay>
</NextLink> </NextLink>
...@@ -44,7 +45,9 @@ const NFTItem = ({ item }: Props) => { ...@@ -44,7 +45,9 @@ const NFTItem = ({ item }: Props) => {
whiteSpace="nowrap" whiteSpace="nowrap"
textOverflow="ellipsis" textOverflow="ellipsis"
> >
{ item.id } <Skeleton isLoaded={ !isLoading }>
{ item.id }
</Skeleton>
</LinkInternal> </LinkInternal>
</TruncatedTextTooltip> </TruncatedTextTooltip>
</Flex> </Flex>
...@@ -53,8 +56,8 @@ const NFTItem = ({ item }: Props) => { ...@@ -53,8 +56,8 @@ const NFTItem = ({ item }: Props) => {
<Flex mb={ 2 } ml={ 1 }> <Flex mb={ 2 } ml={ 1 }>
<Text whiteSpace="pre" variant="secondary" mr={ 2 } lineHeight="24px">Owner</Text> <Text whiteSpace="pre" variant="secondary" mr={ 2 } lineHeight="24px">Owner</Text>
<Address> <Address>
<Hide below="lg" ssr={ false }><AddressIcon address={ item.owner } mr={ 1 }/></Hide> <Hide below="lg" ssr={ false }><AddressIcon address={ item.owner } mr={ 1 } isLoading={ isLoading }/></Hide>
<AddressLink hash={ item.owner.hash } alias={ item.owner.name } type="address" truncation="constant"/> <AddressLink hash={ item.owner.hash } alias={ item.owner.name } type="address" truncation="constant" isLoading={ isLoading }/>
</Address> </Address>
</Flex> </Flex>
) } ) }
......
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