Commit 350147a4 authored by tom's avatar tom

display video

parent 47c7f5d5
......@@ -141,6 +141,9 @@ function makePolicyMap() {
// walletconnect
'*.walletconnect.com',
// token's media
'ipfs.io',
],
'font-src': [
......
......@@ -4,7 +4,7 @@ import React from 'react';
import type { AddressTokenBalance } from 'types/api/address';
import link from 'lib/link/link';
import NftImage from 'ui/shared/NftImage';
import NftImage from 'ui/shared/nft/NftImage';
import TokenLogo from 'ui/shared/TokenLogo';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
......
import { AspectRatio, chakra, Icon, Image, useColorModeValue } from '@chakra-ui/react';
import type { ResponsiveValue } from '@chakra-ui/react';
import { AspectRatio, chakra, Icon, Image, shouldForwardProp, Skeleton, useColorModeValue } from '@chakra-ui/react';
import type { Property } from 'csstype';
import React from 'react';
import nftIcon from 'icons/nft_shield.svg';
......@@ -7,6 +9,7 @@ interface Props {
url: string | null;
className?: string;
fallbackPadding?: string;
objectFit: ResponsiveValue<Property.ObjectFit>;
}
interface FallbackProps {
......@@ -25,7 +28,19 @@ const Fallback = ({ className, padding }: FallbackProps) => {
);
};
const NftImage = ({ url, className, fallbackPadding }: Props) => {
const NftImage = ({ url, className, fallbackPadding, objectFit }: Props) => {
const [ isLoading, setIsLoading ] = React.useState(true);
const handleLoad = React.useCallback(() => {
setIsLoading(false);
}, []);
const handleLoadError = React.useCallback(() => {
setIsLoading(false);
}, []);
const _objectFit = objectFit || 'contain';
return (
<AspectRatio
className={ className }
......@@ -35,20 +50,34 @@ const NftImage = ({ url, className, fallbackPadding }: Props) => {
borderRadius="md"
sx={{
'&>img': {
objectFit: 'contain',
objectFit: _objectFit,
},
}}
>
<Image
w="100%"
h="100%"
objectFit="contain"
objectFit={ _objectFit }
src={ url || undefined }
alt="Token instance image"
fallback={ <Fallback className={ className } padding={ fallbackPadding }/> }
fallback={ url && isLoading ? <Skeleton/> : <Fallback className={ className } padding={ fallbackPadding }/> }
onError={ handleLoadError }
onLoad={ handleLoad }
/>
</AspectRatio>
);
};
export default chakra(NftImage);
const NftImageChakra = chakra(NftImage, {
shouldForwardProp: (prop) => {
const isChakraProp = !shouldForwardProp(prop);
if (isChakraProp && prop !== 'objectFit') {
return false;
}
return true;
},
});
export default NftImageChakra;
import { AspectRatio, chakra, Skeleton } from '@chakra-ui/react';
import React from 'react';
import NftImage from './NftImage';
import NftVideo from './NftVideo';
interface Props {
imageUrl: string | null;
animationUrl: string | null;
className?: string;
}
const NftMedia = ({ imageUrl, animationUrl, className }: Props) => {
const [ type, setType ] = React.useState<'image' | 'video' | undefined>(!animationUrl ? 'image' : undefined);
React.useEffect(() => {
if (!animationUrl) {
return;
}
const xhr = new XMLHttpRequest();
xhr.open('HEAD', animationUrl, true);
xhr.onload = function() {
const contentType = xhr.getResponseHeader('Content-Type');
setType(contentType?.startsWith('video') ? 'video' : 'image');
};
xhr.send();
return () => {
xhr.abort();
};
}, [ animationUrl ]);
if (!type) {
return (
<AspectRatio
className={ className }
ratio={ 1 / 1 }
overflow="hidden"
borderRadius="md"
>
<Skeleton/>
</AspectRatio>
);
}
if (animationUrl && type === 'video') {
return <NftVideo className={ className } src={ animationUrl }/>;
}
return <NftImage className={ className } url={ animationUrl || imageUrl }/>;
};
export default chakra(NftMedia);
import { AspectRatio, Skeleton, chakra } from '@chakra-ui/react';
import React from 'react';
interface Props {
className?: string;
src: string;
}
const NftVideo = ({ className, src }: Props) => {
const [ isLoading, setIsLoading ] = React.useState(true);
const handleCanPlay = React.useCallback(() => {
setIsLoading(false);
}, []);
return (
<AspectRatio
className={ className }
ratio={ 1 / 1 }
overflow="hidden"
borderRadius="md"
>
<>
<chakra.video
src={ src }
autoPlay
disablePictureInPicture
loop
muted
playsInline
onCanPlayThrough={ handleCanPlay }
borderRadius="md"
/>
{ isLoading && <Skeleton position="absolute" w="100%" h="100%" left={ 0 } top={ 0 }/> }
</>
</AspectRatio>
);
};
export default chakra(NftVideo);
......@@ -9,7 +9,7 @@ import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import NftImage from 'ui/shared/NftImage';
import NftMedia from 'ui/shared/nft/NftMedia';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import TokenInstanceCreatorAddress from './details/TokenInstanceCreatorAddress';
......@@ -66,7 +66,13 @@ const TokenInstanceDetails = ({ data, scrollRef }: Props) => {
</DetailsInfoItem>
<TokenInstanceTransfersCount hash={ data.token.address } id={ data.id } onClick={ handleCounterItemClick }/>
</Grid>
<NftImage url={ data.image_url } w="250px" flexShrink={ 0 } alignSelf={{ base: 'center', lg: 'flex-start' }}/>
<NftMedia
imageUrl={ data.image_url }
animationUrl={ data.animation_url }
w="250px"
flexShrink={ 0 }
alignSelf={{ base: 'center', lg: 'flex-start' }}
/>
</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