diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 1a0a19c3a..5fee11f84 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -770,6 +770,12 @@ "contractInteraction": { "message": "Contract interaction" }, + "contractNFT": { + "message": "NFT contract" + }, + "contractRequestingAccess": { + "message": "Contract requesting access" + }, "contractRequestingSpendingCap": { "message": "Contract requesting spending cap" }, diff --git a/ui/components/app/modals/contract-details-modal/contract-details-modal.js b/ui/components/app/modals/contract-details-modal/contract-details-modal.js index dd291303f..9095ef160 100644 --- a/ui/components/app/modals/contract-details-modal/contract-details-modal.js +++ b/ui/components/app/modals/contract-details-modal/contract-details-modal.js @@ -25,6 +25,8 @@ import { import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard'; import UrlIcon from '../../../ui/url-icon/url-icon'; import { getAddressBookEntry } from '../../../../selectors'; +import { ERC1155, ERC721 } from '../../../../../shared/constants/transaction'; +import NftCollectionImage from '../../../ui/nft-collection-image/nft-collection-image'; export default function ContractDetailsModal({ onClose, @@ -35,6 +37,9 @@ export default function ContractDetailsModal({ rpcPrefs, origin, siteImage, + tokenId, + assetName, + assetStandard, }) { const t = useI18nContext(); const [copiedTokenAddress, handleCopyTokenAddress] = useCopyToClipboard(); @@ -43,6 +48,12 @@ export default function ContractDetailsModal({ const addressBookEntry = useSelector((state) => ({ data: getAddressBookEntry(state, toAddress), })); + const nft = + assetStandard === ERC721 || + assetStandard === ERC1155 || + // if we don't have an asset standard but we do have either both an assetname and a tokenID or both a tokenName and tokenId we assume its an NFT + (assetName && tokenId) || + (tokenName && tokenId); return ( @@ -75,7 +86,7 @@ export default function ContractDetailsModal({ marginTop={4} marginBottom={2} > - {t('contractToken')} + {nft ? t('contractNFT') : t('contractToken')} - + {nft ? ( + + + + ) : ( + + )} {ellipsify(tokenAddress)} @@ -166,7 +188,9 @@ export default function ContractDetailsModal({ marginTop={4} marginBottom={2} > - {t('contractRequestingSpendingCap')} + {nft + ? t('contractRequestingAccess') + : t('contractRequestingSpendingCap')} - + {nft ? ( + + ) : ( + + )} {ellipsify(toAddress)} @@ -310,4 +344,16 @@ ContractDetailsModal.propTypes = { * Dapp image */ siteImage: PropTypes.string, + /** + * The token id of the collectible + */ + tokenId: PropTypes.string, + /** + * Token Standard + */ + assetStandard: PropTypes.string, + /** + * The name of the collection + */ + assetName: PropTypes.string, }; diff --git a/ui/components/app/modals/contract-details-modal/index.scss b/ui/components/app/modals/contract-details-modal/index.scss index 52af5a240..1180406c3 100644 --- a/ui/components/app/modals/contract-details-modal/index.scss +++ b/ui/components/app/modals/contract-details-modal/index.scss @@ -1,12 +1,15 @@ .contract-details-modal { - width: 360px !important; + width: 100% !important; + max-width: 408px; &__content { border-bottom: 1px solid var(--color-border-muted); &__contract { &__identicon { - margin: 16px 16px 38px 16px; + margin: 16px; + box-shadow: none; + background: none; } &__identicon-for-unknown-contact { diff --git a/ui/components/ui/nft-collection-image/index.js b/ui/components/ui/nft-collection-image/index.js new file mode 100644 index 000000000..f0e431372 --- /dev/null +++ b/ui/components/ui/nft-collection-image/index.js @@ -0,0 +1 @@ +export { default } from './nft-collection-image'; diff --git a/ui/components/ui/nft-collection-image/index.scss b/ui/components/ui/nft-collection-image/index.scss new file mode 100644 index 000000000..677f3c3f1 --- /dev/null +++ b/ui/components/ui/nft-collection-image/index.scss @@ -0,0 +1,7 @@ +.collection-image-alt { + border-radius: 50%; + width: 24px; + height: 24px; + padding: 2px; + background: var(--color-overlay-alternative); +} diff --git a/ui/components/ui/nft-collection-image/nft-collection-image.js b/ui/components/ui/nft-collection-image/nft-collection-image.js new file mode 100644 index 000000000..8b0c0d25b --- /dev/null +++ b/ui/components/ui/nft-collection-image/nft-collection-image.js @@ -0,0 +1,45 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useSelector } from 'react-redux'; +import Box from '../box'; +import { COLORS, TEXT_ALIGN } from '../../../helpers/constants/design-system'; +import Identicon from '../identicon'; +import { getTokenList } from '../../../selectors'; +import { useCollectiblesCollections } from '../../../hooks/useCollectiblesCollections'; + +export default function NftCollectionImage({ assetName, tokenAddress }) { + const { collections } = useCollectiblesCollections(); + const tokenList = useSelector(getTokenList); + const nftTokenListImage = tokenList[tokenAddress.toLowerCase()]?.iconUrl; + + const renderCollectionImageOrName = () => { + const collection = Object.values(collections).find( + ({ collectionName }) => collectionName === assetName, + ); + + if (collection?.collectionImage || nftTokenListImage) { + return ( + + ); + } + return ( + + {assetName?.[0]?.toUpperCase() ?? null} + + ); + }; + + return {renderCollectionImageOrName()}; +} + +NftCollectionImage.propTypes = { + assetName: PropTypes.string, + tokenAddress: PropTypes.string, +}; diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss index 8cc0e8a0b..f0786f813 100644 --- a/ui/components/ui/ui-components.scss +++ b/ui/components/ui/ui-components.scss @@ -63,3 +63,5 @@ @import 'deprecated-test-networks/index.scss'; @import 'contract-token-values/index.scss'; @import 'nft-info/index.scss'; +@import 'nft-collection-image/index'; + diff --git a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js index 859d8a7ad..e610960bb 100644 --- a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js +++ b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js @@ -32,6 +32,7 @@ import { ERC721, } from '../../../../shared/constants/transaction'; import { CHAIN_IDS, TEST_CHAINS } from '../../../../shared/constants/network'; +import ContractDetailsModal from '../../../components/app/modals/contract-details-modal/contract-details-modal'; export default class ConfirmApproveContent extends Component { static contextTypes = { @@ -82,6 +83,7 @@ export default class ConfirmApproveContent extends Component { state = { showFullTxDetails: false, copied: false, + setshowContractDetails: false, }; renderApproveContentCard({ @@ -588,8 +590,11 @@ export default class ConfirmApproveContent extends Component { isContract, assetStandard, userAddress, + tokenId, + tokenAddress, + assetName, } = this.props; - const { showFullTxDetails } = this.state; + const { showFullTxDetails, setshowContractDetails } = this.state; return (
{this.renderDescription()}
+ {(assetStandard === ERC721 || + assetStandard === ERC1155 || + (assetName && tokenId) || + (tokenSymbol && tokenId)) && ( + + + {setshowContractDetails && ( + this.setState({ setshowContractDetails: false })} + tokenName={tokenSymbol} + tokenAddress={tokenAddress} + toAddress={toAddress} + chainId={chainId} + rpcPrefs={rpcPrefs} + tokenId={tokenId} + assetName={assetName} + assetStandard={assetStandard} + /> + )} + + )}