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