import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { getTokenTrackerLink } from '@metamask/etherscan-link'; import { isEqual } from 'lodash'; import Box from '../../ui/box'; import Card from '../../ui/card'; import Typography from '../../ui/typography/typography'; import { ButtonIcon, ICON_NAMES } from '../../component-library'; import { COLORS, TYPOGRAPHY, FONT_WEIGHT, JUSTIFY_CONTENT, FLEX_DIRECTION, OVERFLOW_WRAP, DISPLAY, BLOCK_SIZES, } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getAssetImageURL, shortenAddress } from '../../../helpers/utils/util'; import { getCollectibleImageAlt } from '../../../helpers/utils/collectibles'; import { getCurrentChainId, getIpfsGateway, getRpcPrefsForCurrentProvider, getSelectedIdentity, } from '../../../selectors'; import AssetNavigation from '../../../pages/asset/components/asset-navigation'; import { getCollectibleContracts } from '../../../ducks/metamask/metamask'; import { DEFAULT_ROUTE, SEND_ROUTE } from '../../../helpers/constants/routes'; import { checkAndUpdateSingleNftOwnershipStatus, removeAndIgnoreNft, setRemoveCollectibleMessage, } from '../../../store/actions'; import { CHAIN_IDS } from '../../../../shared/constants/network'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import CollectibleOptions from '../collectible-options/collectible-options'; import Button from '../../ui/button'; import { startNewDraftTransaction } from '../../../ducks/send'; import InfoTooltip from '../../ui/info-tooltip'; import { usePrevious } from '../../../hooks/usePrevious'; import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import { AssetType, TokenStandard, } from '../../../../shared/constants/transaction'; import CollectibleDefaultImage from '../collectible-default-image'; export default function CollectibleDetails({ collectible }) { const { image, imageOriginal, name, description, address, tokenId, standard, isCurrentlyOwned, } = collectible; const t = useI18nContext(); const history = useHistory(); const dispatch = useDispatch(); const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); const ipfsGateway = useSelector(getIpfsGateway); const collectibleContracts = useSelector(getCollectibleContracts); const currentNetwork = useSelector(getCurrentChainId); const [sourceCopied, handleSourceCopy] = useCopyToClipboard(); const [addressCopied, handleAddressCopy] = useCopyToClipboard(); const collectibleContractName = collectibleContracts.find( ({ address: contractAddress }) => isEqualCaseInsensitive(contractAddress, address), )?.name; const selectedAccountName = useSelector( (state) => getSelectedIdentity(state).name, ); const collectibleImageURL = getAssetImageURL( imageOriginal ?? image, ipfsGateway, ); const collectibleImageAlt = getCollectibleImageAlt(collectible); const isDataURI = collectibleImageURL.startsWith('data:'); const onRemove = () => { dispatch(removeAndIgnoreNft(address, tokenId)); dispatch(setRemoveCollectibleMessage('success')); history.push(DEFAULT_ROUTE); }; const prevCollectible = usePrevious(collectible); useEffect(() => { if (!isEqual(prevCollectible, collectible)) { checkAndUpdateSingleNftOwnershipStatus(collectible); } }, [collectible, prevCollectible]); const getOpenSeaLink = () => { switch (currentNetwork) { case CHAIN_IDS.MAINNET: return `https://opensea.io/assets/${address}/${tokenId}`; case CHAIN_IDS.POLYGON: return `https://opensea.io/assets/matic/${address}/${tokenId}`; case CHAIN_IDS.GOERLI: case CHAIN_IDS.SEPOLIA: return `https://testnets.opensea.io/assets/${address}/${tokenId}`; default: return null; } }; const openSeaLink = getOpenSeaLink(); const sendDisabled = standard !== TokenStandard.ERC721; const inPopUp = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP; const onSend = async () => { await dispatch( startNewDraftTransaction({ type: AssetType.NFT, details: collectible, }), ); history.push(SEND_ROUTE); }; const renderSendButton = () => { if (isCurrentlyOwned === false) { return
; } return ( {sendDisabled ? ( ) : null} ); }; return ( <> history.push(DEFAULT_ROUTE)} optionsButton={ global.platform.openTab({ url: openSeaLink }) : null } onRemove={onRemove} /> } />
{image ? ( {collectibleImageAlt} ) : ( )}
{name} #{tokenId}
{description ? (
{t('description')} {description}
) : null} {inPopUp ? null : renderSendButton()}
{t('source')} {isDataURI ? ( <>{collectibleImageURL} ) : ( {collectibleImageURL} )} { handleSourceCopy(collectibleImageURL); }} iconName={ sourceCopied ? ICON_NAMES.COPY_SUCCESS_FILLED : ICON_NAMES.COPY_FILLED } /> {t('contractAddress')} {inPopUp ? shortenAddress(address) : address} { handleAddressCopy(address); }} iconName={ addressCopied ? ICON_NAMES.COPY_SUCCESS_FILLED : ICON_NAMES.COPY_FILLED } /> {inPopUp ? renderSendButton() : null} {t('nftDisclaimer')}
); } CollectibleDetails.propTypes = { collectible: PropTypes.shape({ address: PropTypes.string.isRequired, tokenId: PropTypes.string.isRequired, isCurrentlyOwned: PropTypes.bool, name: PropTypes.string, description: PropTypes.string, image: PropTypes.string, standard: PropTypes.string, imageThumbnail: PropTypes.string, imagePreview: PropTypes.string, imageOriginal: PropTypes.string, creator: PropTypes.shape({ address: PropTypes.string, config: PropTypes.string, profile_img_url: PropTypes.string, }), }), };