import { ArrowOutwardRounded, AudiotrackRounded, ExpandCircleDownOutlined, Image, ImageNotSupported, InfoOutlined, } from '@mui/icons-material' import clsx from 'clsx' import NextImage from 'next/image' import { forwardRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ReactPlayer from 'react-player' import { NftCardProps } from '@dao-dao/types' import { NFT_VIDEO_EXTENSIONS, getImageUrlForChainId, getNftName, objectMatchesStructure, toAccessibleImageUrl, } from '@dao-dao/utils' import { AudioPlayer } from '../AudioPlayer' import { Button } from '../buttons' import { CopyToClipboardUnderline } from '../CopyToClipboard' import { Checkbox } from '../inputs' import { LinkWrapper } from '../LinkWrapper' import { MarkdownRenderer } from '../MarkdownRenderer' import { ButtonPopup } from '../popup/ButtonPopup' import { TokenAmountDisplay } from '../token' import { Tooltip } from '../tooltip' import { TooltipInfoIcon } from '../tooltip/TooltipInfoIcon' import { TooltipLikeDisplay } from '../tooltip/TooltipLikeDisplay' export const NftCard = forwardRef( function NftCard( { hideCollection, ownerLabel, collectionAddress, collectionName, owner, externalLink, checkbox, imageUrl, metadata, highestOffer, fetchedTimestamp, name, description, tokenId, chainId, className, EntityDisplay, buttonPopup, banner, bannerTooltip, }, ref ) { const { t } = useTranslation() // Loading if imageUrl is present. const [imageLoading, setImageLoading] = useState(!!imageUrl) const chainImage = getImageUrlForChainId(chainId) const [descriptionCollapsible, setDescriptionCollapsible] = useState(false) const [descriptionCollapsed, setDescriptionCollapsed] = useState(true) const video = // If image contains a video, treat it as a video. imageUrl && NFT_VIDEO_EXTENSIONS.includes(imageUrl.split('.').pop() || '') ? imageUrl : metadata && objectMatchesStructure(metadata, { properties: { video: {}, }, }) ? metadata.properties.video : null const audio = metadata && objectMatchesStructure(metadata, { properties: { audio: {}, }, }) ? metadata.properties.audio : null return (
{banner && (
{/* Background */}
{!!bannerTooltip && ( )}

{banner}

)}
{video ? ( setImageLoading(false)} url={toAccessibleImageUrl(video)} width="100%" /> ) : imageUrl ? ( setImageLoading(false)} // NFTs tend to be shown in the `GridCardContainer` component, // which shows 1 column until 640px, 2 columns until 768px, // and 3 columns after that (using the `sm` and `md` tailwind // breakpoints). This helps the browser optimize the image. sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, 33vw" src={toAccessibleImageUrl(imageUrl, { proxy: true })} /> ) : (
{audio ? ( ) : ( )}
)}
{audio && !video && ( )}
{externalLink && ( e.stopPropagation()} openInNewTab > } label={t('button.openInDestination', { destination: externalLink.name, })} /> )} {buttonPopup && buttonPopup.sections.length > 0 && (
({ Icon: ExpandCircleDownOutlined, className: clsx( 'group-hover/nft:opacity-100 shadow-dp4', !open && 'opacity-0' ), variant: 'primary_inverted', }), }} />
)}
{checkbox && ( )} {(!hideCollection || (owner && EntityDisplay) || highestOffer) && (
{/* Collection */} {!hideCollection && (

{t('title.collection')}

{chainImage && ( )}
)} {/* Owner */} {owner && EntityDisplay && (

{ownerLabel || t('title.owner')}

)} {/* Highest offer */} {highestOffer && (

{t('title.highestOffer')}

{highestOffer.amount && highestOffer.amount.isPositive() && highestOffer.offerToken && ( )} {highestOffer.amountUsd && (
)}
)}
)}

{getNftName(collectionName, tokenId, name)}

{!!description && (
{ if (!ref || descriptionCollapsible) { return } setDescriptionCollapsible( ref.scrollHeight > ref.clientHeight ) } } /> {descriptionCollapsible && ( )}
)}
) } ) export const NftCardLoader = () => (
{/* eslint-disable-next-line jsx-a11y/alt-text */}
)