import React, { useCallback, useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { ethErrors, serializeError } from 'eth-rpc-errors'; import { getTokenTrackerLink } from '@metamask/etherscan-link'; import classnames from 'classnames'; import { PageContainerFooter } from '../../components/ui/page-container'; import { I18nContext } from '../../contexts/i18n'; import { MetaMetricsContext } from '../../contexts/metametrics'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { resolvePendingApproval, rejectPendingApproval, } from '../../store/actions'; import { MetaMetricsEventCategory, MetaMetricsEventName, MetaMetricsTokenEventSource, } from '../../../shared/constants/metametrics'; import { AssetType } from '../../../shared/constants/transaction'; import { BUTTON_SIZES, ButtonIcon, ButtonIconSize, ButtonLink, IconName, Box, Text, } from '../../components/component-library'; import { getCurrentChainId, getRpcPrefsForCurrentProvider, getSuggestedNfts, getIpfsGateway, getNetworkIdentifier, getSelectedAddress, getSelectedAccountCachedBalance, getAddressBookEntryOrAccountName, } from '../../selectors'; import NftDefaultImage from '../../components/app/nft-default-image/nft-default-image'; import { getAssetImageURL, shortenAddress } from '../../helpers/utils/util'; import { AlignItems, BorderRadius, Display, FlexDirection, FlexWrap, IconColor, JustifyContent, TextAlign, TextVariant, BlockSize, } from '../../helpers/constants/design-system'; import NetworkAccountBalanceHeader from '../../components/app/network-account-balance-header/network-account-balance-header'; import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network'; import SiteOrigin from '../../components/ui/site-origin/site-origin'; import { PRIMARY } from '../../helpers/constants/common'; import { useUserPreferencedCurrency } from '../../hooks/useUserPreferencedCurrency'; import { useCurrencyDisplay } from '../../hooks/useCurrencyDisplay'; import { useOriginMetadata } from '../../hooks/useOriginMetadata'; const ConfirmAddSuggestedNFT = () => { const t = useContext(I18nContext); const dispatch = useDispatch(); const history = useHistory(); const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage); const suggestedNfts = useSelector(getSuggestedNfts); const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); const chainId = useSelector(getCurrentChainId); const ipfsGateway = useSelector(getIpfsGateway); const trackEvent = useContext(MetaMetricsContext); const networkIdentifier = useSelector(getNetworkIdentifier); const selectedAddress = useSelector(getSelectedAddress); const selectedAccountBalance = useSelector(getSelectedAccountCachedBalance); const accountName = useSelector((state) => getAddressBookEntryOrAccountName(state, selectedAddress), ); const networkName = NETWORK_TO_NAME_MAP[chainId] || networkIdentifier; const { currency: primaryCurrency, numberOfDecimals: primaryNumberOfDecimals, } = useUserPreferencedCurrency(PRIMARY, { ethNumberOfDecimals: 4 }); const [primaryCurrencyValue] = useCurrencyDisplay(selectedAccountBalance, { numberOfDecimals: primaryNumberOfDecimals, currency: primaryCurrency, }); const originMetadata = useOriginMetadata(suggestedNfts[0]?.origin) || {}; const handleAddNftsClick = useCallback(async () => { await Promise.all( suggestedNfts.map(async ({ requestData: { asset }, id }) => { await dispatch(resolvePendingApproval(id, null)); trackEvent({ event: MetaMetricsEventName.NftAdded, category: MetaMetricsEventCategory.Wallet, sensitiveProperties: { token_contract_address: asset.address, token_symbol: asset.symbol, token_id: asset.tokenId, token_standard: asset.standard, asset_type: AssetType.NFT, source: MetaMetricsTokenEventSource.Dapp, }, }); }), ); history.push(mostRecentOverviewPage); }, [dispatch, history, trackEvent, mostRecentOverviewPage, suggestedNfts]); const handleCancelNftClick = useCallback(async () => { await Promise.all( suggestedNfts.map(async ({ id }) => { return dispatch( rejectPendingApproval( id, serializeError(ethErrors.provider.userRejectedRequest()), ), ); }), ); history.push(mostRecentOverviewPage); }, [dispatch, history, mostRecentOverviewPage, suggestedNfts]); useEffect(() => { const goBackIfNoSuggestedNftsOnFirstRender = () => { if (!suggestedNfts.length) { history.push(mostRecentOverviewPage); } }; goBackIfNoSuggestedNftsOnFirstRender(); }, [history, mostRecentOverviewPage, suggestedNfts]); let origin; let link; if (suggestedNfts.length) { try { const url = new URL(suggestedNfts[0].origin); origin = url.host; link = url.href; } catch { origin = 'dapp'; } } return ( {t('addSuggestedNFTs')} {t('wantsToAddThisAsset', [ origin === 'dapp' ? ( {origin} ) : ( {origin} ), ])} 1, })} > {suggestedNfts.map( ({ id, requestData: { asset: { address, tokenId, symbol, image, name }, }, }) => { const nftImageURL = getAssetImageURL(image, ipfsGateway); const blockExplorerLink = getTokenTrackerLink( address, chainId, null, null, { blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null, }, ); if (suggestedNfts.length === 1) { return ( {nftImageURL ? ( {name ) : ( )} {rpcPrefs.blockExplorerUrl ? ( {name || symbol || shortenAddress(address)} ) : ( {name || symbol || shortenAddress(address)} )} #{tokenId} ); } return ( {nftImageURL ? ( {name ) : ( )} {rpcPrefs.blockExplorerUrl ? ( {name || symbol || shortenAddress(address)} ) : ( {name || symbol || shortenAddress(address)} )} #{tokenId} { e.preventDefault(); e.stopPropagation(); dispatch( rejectPendingApproval( id, serializeError( ethErrors.provider.userRejectedRequest(), ), ), ); }} /> ); }, )} ); }; export default ConfirmAddSuggestedNFT;