Commit 41dc5ea7 authored by tom's avatar tom

mobile view

parent 9939ef54
import { Box, Tag } from '@chakra-ui/react';
import { Box, Tag, Icon } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import nftIcon from 'icons/nft_shield.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import TextAd from 'ui/shared/ad/TextAd';
import AddressHeadingInfo from 'ui/shared/AddressHeadingInfo';
import LinkExternal from 'ui/shared/LinkExternal';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import TokenLogo from 'ui/shared/TokenLogo';
import TokenTransfer from 'ui/token/TokenTransfer/TokenTransfer';
import TokenInstanceDetails from './TokenInstanceDetails';
......@@ -64,7 +65,7 @@ const TokenInstanceContent = () => {
return <TokenInstanceSkeleton/>;
}
const tokenLogo = <TokenLogo hash={ tokenInstanceQuery.data.token.address } name={ tokenInstanceQuery.data.token.name } boxSize={ 6 }/>;
const nftShieldIcon = <Icon as={ nftIcon } boxSize={ 6 }/>;
const tokenTag = <Tag>{ tokenInstanceQuery.data.token.type }</Tag>;
const address = {
hash: hash || '',
......@@ -72,6 +73,31 @@ const TokenInstanceContent = () => {
implementation_name: null,
watchlist_names: [],
};
const appLink = (() => {
if (!tokenInstanceQuery.data.external_app_url) {
return null;
}
try {
const url = new URL(tokenInstanceQuery.data.external_app_url);
return (
<Box fontSize="sm" mt={ 6 }>
<span>View in app </span>
<LinkExternal href={ tokenInstanceQuery.data.external_app_url }>
{ url.hostname }
</LinkExternal>
</Box>
);
} catch (error) {
return (
<Box fontSize="sm" mt={ 6 }>
<LinkExternal href={ tokenInstanceQuery.data.external_app_url }>
View in app
</LinkExternal>
</Box>
);
}
})();
return (
<>
......@@ -80,12 +106,14 @@ const TokenInstanceContent = () => {
text={ `${ tokenInstanceQuery.data.token.name } #${ tokenInstanceQuery.data.id }` }
backLinkUrl={ hasGoBackLink ? appProps.referrer : undefined }
backLinkLabel="Back to token page"
additionalsLeft={ tokenLogo }
additionalsLeft={ nftShieldIcon }
additionalsRight={ tokenTag }
/>
<AddressHeadingInfo address={ address } token={ tokenInstanceQuery.data.token }/>
{ appLink }
<TokenInstanceDetails data={ tokenInstanceQuery.data } scrollRef={ scrollRef }/>
{ /* should stay before tabs to scroll up with pagination */ }
......
......@@ -34,6 +34,16 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
const hasMetadata = metadata && Boolean((metadata.name || metadata.description || metadata.attributes));
const attributeBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const divider = (
<GridItem
colSpan={{ base: undefined, lg: 2 }}
mt={{ base: 2, lg: 3 }}
mb={{ base: 0, lg: 3 }}
borderBottom="1px solid"
borderColor="divider"
/>
);
return (
<>
<Flex alignItems="flex-start" mt={ 8 } flexDir={{ base: 'column-reverse', lg: 'row' }} columnGap={ 6 } rowGap={ 6 }>
......@@ -73,7 +83,6 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
</Flex>
</DetailsInfoItem>
<TokenInstanceTransfersCount hash={ data.token.address } id={ data.id } onClick={ handleCounterItemClick }/>
<DetailsSponsoredItem/>
</Grid>
<NftMedia
imageUrl={ data.image_url }
......@@ -83,56 +92,63 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
alignSelf={{ base: 'center', lg: 'flex-start' }}
/>
</Flex>
{ hasMetadata && (
<Grid
mt={ 8 }
columnGap={ 8 }
rowGap={{ base: 1, lg: 3 }}
templateColumns={{ base: 'minmax(0, 1fr)', lg: '200px minmax(0, 1fr)' }}
overflow="hidden"
>
{ metadata?.name && (
<DetailsInfoItem
title="Name"
hint="NFT name"
whiteSpace="normal"
>
{ metadata.name }
</DetailsInfoItem>
) }
{ metadata?.description && (
<DetailsInfoItem
title="Description"
hint="NFT description"
whiteSpace="normal"
>
{ metadata.description }
</DetailsInfoItem>
) }
{ metadata?.attributes && (
<DetailsInfoItem
title="Attributes"
hint="NFT attributes"
whiteSpace="normal"
>
<Grid gap={ 2 } templateColumns="repeat(auto-fit, minmax(160px, 1fr))" w="100%">
{ metadata.attributes.map((attribute, index) => (
<GridItem
key={ index }
bgColor={ attributeBgColor }
borderRadius="md"
px={ 4 }
py={ 2 }
>
<Box fontSize="xs" color="text_secondary" fontWeight={ 500 }>{ attribute.trait_type }</Box>
<Box fontSize="sm">{ attribute.value }</Box>
</GridItem>
)) }
</Grid>
</DetailsInfoItem>
) }
</Grid>
) }
<Grid
mt={ 8 }
columnGap={ 8 }
rowGap={{ base: 1, lg: 3 }}
templateColumns={{ base: 'minmax(0, 1fr)', lg: '200px minmax(0, 1fr)' }}
overflow="hidden"
>
{ divider }
<DetailsSponsoredItem/>
{ hasMetadata && (
<>
{ divider }
{ metadata?.name && (
<DetailsInfoItem
title="Name"
hint="NFT name"
whiteSpace="normal"
wordBreak="break-word"
>
{ metadata.name }
</DetailsInfoItem>
) }
{ metadata?.description && (
<DetailsInfoItem
title="Description"
hint="NFT description"
whiteSpace="normal"
wordBreak="break-word"
>
{ metadata.description }
</DetailsInfoItem>
) }
{ metadata?.attributes && (
<DetailsInfoItem
title="Attributes"
hint="NFT attributes"
whiteSpace="normal"
>
<Grid gap={ 2 } templateColumns="repeat(auto-fit, minmax(160px, 1fr))" w="100%">
{ metadata.attributes.map((attribute, index) => (
<GridItem
key={ index }
bgColor={ attributeBgColor }
borderRadius="md"
px={ 4 }
py={ 2 }
>
<Box fontSize="xs" color="text_secondary" fontWeight={ 500 }>{ attribute.trait_type }</Box>
<Box fontSize="sm">{ attribute.value }</Box>
</GridItem>
)) }
</Grid>
</DetailsInfoItem>
) }
</>
) }
</Grid>
</>
);
};
......
......@@ -6,7 +6,7 @@ import type { TokenInstance } from 'types/api/token';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
import TokenInstanceMetadataAccordion from './metadata/TokenInstanceMetadataAccordion';
import MetadataAccordion from './metadata/MetadataAccordion';
type Format = 'JSON' | 'Table'
......@@ -26,7 +26,7 @@ const TokenInstanceMetadata = ({ data }: Props) => {
}
const content = format === 'Table' ?
<TokenInstanceMetadataAccordion data={ data }/> :
<MetadataAccordion data={ data }/> :
<RawDataSnippet data={ JSON.stringify(data, undefined, 4) } showCopy={ false }/>;
return (
......
......@@ -11,7 +11,6 @@ interface Props {
}
const MetadataAccordion = ({ data, level = 0 }: Props) => {
const ml = (() => {
if (level === 0) {
return 0;
......@@ -31,12 +30,12 @@ const MetadataAccordion = ({ data, level = 0 }: Props) => {
case 'string':
case 'number':
case 'boolean': {
return <MetadataItemPrimitive name={ name } value={ value } isFlat={ isFlat }/>;
return <MetadataItemPrimitive name={ name } value={ value } isFlat={ isFlat } level={ level }/>;
}
case 'object': {
if (value === null) {
return <MetadataItemPrimitive name={ name } value={ value } isFlat={ isFlat }/>;
return <MetadataItemPrimitive name={ name } value={ value } isFlat={ isFlat } level={ level }/>;
}
if (Array.isArray(value) && value.length > 0) {
......@@ -49,13 +48,13 @@ const MetadataAccordion = ({ data, level = 0 }: Props) => {
}
// eslint-disable-next-line no-fallthrough
default: {
return <MetadataItemPrimitive name={ name } value={ String(value) } isFlat={ isFlat }/>;
return <MetadataItemPrimitive name={ name } value={ String(value) } isFlat={ isFlat } level={ level }/>;
}
}
}, [ level, isFlat ]);
return (
<Accordion allowMultiple fontSize="sm" ml={ `${ ml }px` } defaultIndex={ level === 0 ? [ 0 ] : undefined }>
<Accordion allowMultiple fontSize="sm" ml={{ base: level === 0 ? 0 : 6, lg: `${ ml }px` }} defaultIndex={ level === 0 ? [ 0 ] : undefined }>
{ Object.entries(data).map(([ key, value ]) => renderItem(key, value)) }
</Accordion>
);
......
......@@ -5,20 +5,23 @@ interface Props {
children: React.ReactNode;
level?: number;
className?: string;
isFlat?: boolean;
}
const MetadataAccordionItem = ({ children, className, level }: Props) => {
const MetadataAccordionItem = ({ children, className, level, isFlat }: Props) => {
return (
<AccordionItem
className={ className }
display="flex"
alignItems="flex-start"
flexDir={{ base: 'column', lg: 'row' }}
py={ 2 }
pl={{ base: 0, lg: 6 }}
pl={ isFlat ? 0 : 6 }
columnGap={ 3 }
borderTopWidth="1px"
borderColor="divider"
wordBreak="break-all"
rowGap={ 1 }
_last={{
borderBottomWidth: level === 0 ? '1px' : '0px',
}}
......
......@@ -10,7 +10,7 @@ interface Props {
const MetadataAccordionItemTitle = ({ name, className }: Props) => {
return (
<Box w="90px" flexShrink={ 0 } fontWeight={ 600 } wordBreak="break-word" className={ className }>
<Box w={{ base: 'auto', lg: '90px' }} flexShrink={ 0 } fontWeight={ 600 } wordBreak="break-word" className={ className }>
{ formatName(name) }
</Box>
);
......
......@@ -15,7 +15,7 @@ const MetadataItemArray = ({ name, value, level }: Props) => {
return (
<MetadataAccordionItem
flexDir="column"
flexDir={{ lg: 'column' }}
alignItems="stretch"
pl={{ base: 0, lg: 0 }}
py={ 0 }
......@@ -34,14 +34,14 @@ const MetadataItemArray = ({ name, value, level }: Props) => {
<AccordionIcon boxSize={ 6 } p={ 1 }/>
<MetadataAccordionItemTitle name={ name }/>
</AccordionButton>
<AccordionPanel p={ 0 } ml={ level === 0 ? '126px' : '24px' }>
<AccordionPanel p={ 0 } ml={{ base: 6, lg: level === 0 ? '126px' : 6 }}>
{ value.map((item, index) => {
const content = (() => {
switch (typeof item) {
case 'string':
case 'number':
case 'boolean': {
return <MetadataItemPrimitive value={ item } isItem={ false }/>;
return <MetadataItemPrimitive value={ item } isItem={ false } level={ level }/>;
}
case 'object': {
if (item) {
......@@ -51,8 +51,12 @@ const MetadataItemArray = ({ name, value, level }: Props) => {
return Object.entries(item).map(([ name, value ], index) => {
return (
<Flex key={ index } columnGap={ 3 }>
<MetadataAccordionItemTitle name={ name } fontWeight={ 400 }/>
<MetadataItemPrimitive value={ typeof value === 'object' ? JSON.stringify(value, undefined, 2) : value } isItem={ false }/>
<MetadataAccordionItemTitle name={ name } fontWeight={ 400 } w={{ base: '90px' }}/>
<MetadataItemPrimitive
value={ typeof value === 'object' ? JSON.stringify(value, undefined, 2) : value }
isItem={ false }
level={ level }
/>
</Flex>
);
});
......
......@@ -15,7 +15,7 @@ const MetadataItemObject = ({ name, value, level }: Props) => {
if (level >= 4) {
return (
<MetadataAccordionItem level={ level } pl={{ base: 0, lg: 0 }}>
<MetadataAccordionItem level={ level } isFlat>
<MetadataAccordionItemTitle name={ name }/>
<Box whiteSpace="pre-wrap">{ JSON.stringify(value, undefined, 2) }</Box>
</MetadataAccordionItem>
......@@ -24,10 +24,10 @@ const MetadataItemObject = ({ name, value, level }: Props) => {
return (
<MetadataAccordionItem
flexDir="column"
flexDir={{ lg: 'column' }}
alignItems="stretch"
pl={{ base: 0, lg: 0 }}
py={ 0 }
isFlat
level={ level }
>
<AccordionButton
......
......@@ -12,9 +12,10 @@ interface Props {
value: Primitive;
isItem?: boolean;
isFlat?: boolean;
level: number;
}
const MetadataItemPrimitive = ({ name, value, isItem = true, isFlat }: Props) => {
const MetadataItemPrimitive = ({ name, value, isItem = true, isFlat, level }: Props) => {
const Component = isItem ? MetadataAccordionItem : Box;
......@@ -37,7 +38,7 @@ const MetadataItemPrimitive = ({ name, value, isItem = true, isFlat }: Props) =>
})();
return (
<Component { ...(isFlat ? { pl: { base: 0, lg: 0 } } : {}) }>
<Component level={ level } isFlat={ isFlat }>
{ name && <MetadataAccordionItemTitle name={ name }/> }
{ content }
</Component>
......
......@@ -154,11 +154,11 @@ const data = {
// value: 'F',
// trait_type: 'Player Position',
// },
// {
// value: '15',
// trait_type: 'Player Jersey Number',
// display_type: 'number',
// },
{
value: '15',
trait_type: 'Player Jersey Number',
display_type: 'number',
},
// {
// value: 'Freshman',
// trait_type: 'Player Year',
......
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