Skip to content

Commit

Permalink
fix: display msg if tokenId already exists
Browse files Browse the repository at this point in the history
  • Loading branch information
sahar-fehri committed Sep 19, 2023
1 parent 4ecc600 commit d170aa8
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 3 deletions.
3 changes: 3 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion ui/components/multichain/import-nfts-modal/import-nfts-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ import {
BannerAlert,
} from '../../component-library';
import Tooltip from '../../ui/tooltip';
import { useNftsCollections } from '../../../hooks/useNftsCollections';
import { tokenIdExist } from '../../../helpers/utils/util';

export const ImportNftsModal = ({ onClose }) => {
const t = useI18nContext();
Expand All @@ -68,12 +70,14 @@ export const ImportNftsModal = ({ onClose }) => {
tokenId: initialTokenId,
ignoreErc20Token,
} = useSelector((state) => state.appState.importNftsModal);
const existingNfts = useNftsCollections();

const [nftAddress, setNftAddress] = useState(initialTokenAddress ?? '');
const [tokenId, setTokenId] = useState(initialTokenId ?? '');
const [disabled, setDisabled] = useState(true);
const [nftAddFailed, setNftAddFailed] = useState(false);
const trackEvent = useContext(MetaMetricsContext);
const [duplicateTokenIdError, setDuplicateTokenIdError] = useState(null);

const handleAddNft = async () => {
try {
Expand Down Expand Up @@ -135,7 +139,23 @@ export const ImportNftsModal = ({ onClose }) => {
};

const validateAndSetTokenId = (val) => {
setDisabled(!isValidHexAddress(nftAddress) || !val || isNaN(Number(val)));
setDuplicateTokenIdError(null);
// Check if tokenId is already imported
const tokenIdExists = tokenIdExist(
nftAddress,
val,
existingNfts.collections,
);
if (tokenIdExists) {
setDuplicateTokenIdError(t('nftAlreadyAdded'));
}
setDisabled(
!isValidHexAddress(nftAddress) ||
!val ||
isNaN(Number(val)) ||
tokenIdExists,
);

setTokenId(val);
};

Expand Down Expand Up @@ -243,6 +263,8 @@ export const ImportNftsModal = ({ onClose }) => {
validateAndSetTokenId(e.target.value);
setNftAddFailed(false);
}}
helpText={duplicateTokenIdError}
error={duplicateTokenIdError}
/>
</Box>
</Box>
Expand Down
40 changes: 38 additions & 2 deletions ui/helpers/utils/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import bowser from 'bowser';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { getSnapPrefix } from '@metamask/snaps-utils';
import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/rpc-methods';
import { isObject } from '@metamask/utils';
import { isObject, isStrictHexString } from '@metamask/utils';
///: END:ONLY_INCLUDE_IN
import { CHAIN_IDS, NETWORK_TYPES } from '../../../shared/constants/network';
import {
Expand All @@ -30,7 +30,6 @@ import {
SNAPS_METADATA,
} from '../../../shared/constants/snaps';
///: END:ONLY_INCLUDE_IN

// formatData :: ( date: <Unix Timestamp> ) -> String
export function formatDate(date, format = "M/d/y 'at' T") {
if (!date) {
Expand Down Expand Up @@ -631,3 +630,40 @@ export const getNetworkNameFromProviderType = (providerName) => {
export const isAbleToExportAccount = (keyringType = '') => {
return !keyringType.includes('Hardware') && !keyringType.includes('Snap');
};

/**
* Checks if a tokenId in Hex or decimal format already exists in an object.
*
* @param {*} address - collection address.
* @param {*} tokId - tokenId to search for
* @param {*} obj - object to look into
* @returns {boolean} `false` if tokenId does not already exist.
*/
export const tokenIdExist = (address, tokId, obj) => {
// check if input tokenId is hexadecimal
// If it is convert to decimal and compare with existing tokens
// if it is decimal convert to hexadecimal and compare
const isHex = isStrictHexString(tokId);
let convertedTokenId;
if (isHex) {
// Convert to decimal
convertedTokenId = parseInt(tokId, 16);
} else {
// Convert to hex
const decimalNumber = parseInt(tokId, 10); // 10 is the base for decimal
convertedTokenId = `0x${decimalNumber.toString(16)}`;
}

if (obj[address]) {
const value = obj[address];
return lodash.some(value.nfts, (nft) => {
return (
nft.address === address &&
(nft.tokenId.toLowerCase() === tokId.toLowerCase() ||
nft.tokenId.toLowerCase() ===
convertedTokenId.toString().toLowerCase())
);
});
}
return false;
};
68 changes: 68 additions & 0 deletions ui/helpers/utils/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -929,4 +929,72 @@ describe('util', () => {
expect(util.getNetworkNameFromProviderType('rpc')).toStrictEqual('');
});
});

describe('tokenIdExist()', () => {
const data = {
'0x2df920B180c58766951395c26ecF1EC2063490Fa': {
collectionName: 'Numbers',
nfts: [
{
address: '0x2df920B180c58766951395c26ecF1EC2063490Fa',
description: 'Numbers',
favorite: false,
name: 'Numbers #132',
tokenId: '132',
},
],
},
'0x2df920B180c58766951395c26ecF1EC206343334': {
collectionName: 'toto',
nfts: [
{
address: '0x2df920B180c58766951395c26ecF1EC206343334',
description: 'toto',
favorite: false,
name: 'toto#3453',
tokenId: '3453',
},
],
},
};
it('should return true if it exists', () => {
expect(
util.tokenIdExist(
'0x2df920B180c58766951395c26ecF1EC206343334',
'3456',
data,
),
).toBeTruthy();
});

it('should return true if it exists in decimal format', () => {
expect(
util.tokenIdExist(
'0x2df920B180c58766951395c26ecF1EC206343334',
'0xD80',
data,
),
).toBeTruthy();
});

it('should return false if it does not exists', () => {
expect(
util.tokenIdExist(
'0x2df920B180c58766951395c26ecF1EC206343334',
'1122',
data,
),
).toBeFalsy();
});

it('should return false if it address does not exists', () => {
expect(
util.tokenIdExist(
'0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
'1122',
data,
),
).toBeFalsy();
});
});
});

0 comments on commit d170aa8

Please sign in to comment.