diff --git a/src/components/wallet/collectibles/CollectibleList.tsx b/src/components/wallet/collectibles/CollectibleList.tsx index 2b4e1fe..d0b48d6 100644 --- a/src/components/wallet/collectibles/CollectibleList.tsx +++ b/src/components/wallet/collectibles/CollectibleList.tsx @@ -97,7 +97,15 @@ export default function CollectibleList({ {isImportCollectibleViewOpen && ( - setIsImportCollectibleViewOpen(false)}> + setIsImportCollectibleViewOpen(false)} + contentProps={{ + style: { + scrollbarColor: 'gray black', + scrollbarWidth: 'thin' + } + }} + > setIsImportCollectibleViewOpen(false)} /> )} diff --git a/src/components/wallet/collectibles/ImportCollectible.tsx b/src/components/wallet/collectibles/ImportCollectible.tsx index 397ab5f..eedadf6 100644 --- a/src/components/wallet/collectibles/ImportCollectible.tsx +++ b/src/components/wallet/collectibles/ImportCollectible.tsx @@ -2,6 +2,7 @@ import { Box, Button, Card, + ChevronLeftIcon, Divider, Image, SearchIcon, @@ -12,6 +13,7 @@ import { useToast } from '@0xsequence/design-system' import { NetworkConfig, NetworkType } from '@0xsequence/network' +import { BigNumberish, ethers } from 'ethers' import { ChangeEvent, useEffect, useRef, useState } from 'react' import { useObservable, useStore } from '~/stores' @@ -35,27 +37,42 @@ export default function ImportCollectible({ onClose }: { onClose: () => void }) const toast = useToast() - const [selectedNetwork, setSelectedNetwork] = useState() + const [selectedNetwork, setSelectedNetwork] = useState(mainnetNetworks[0]) const [collectibleManualAddress, setCollectibleManualAddress] = useState() const [collectibleManualTokenId, setCollectibleManualTokenId] = useState() - const [contractType, setContractType] = useState() - - const [collectibleInfo, setCollectibleInfo] = useState() + const [contractType, setContractType] = useState( + CollectibleContractTypeValues.ERC721 + ) - const [isAddingCollectible, setIsAddingCollectible] = useState(false) - const [isAddingCollectibleManually, setIsAddingCollectibleManually] = useState(false) + const [isAddingCollection, setIsAddingCollection] = useState(false) const [collectionList, setCollectionList] = useState([]) const [collectionListFilter, setCollectionListFilter] = useState('') const [filteredCollectionList, setFilteredCollectionList] = useState([]) - const [selectedCollection, setSelectedCollection] = useState([]) + const [selectedCollection, setSelectedCollection] = useState() + const [selectedCollectibles, setSelectedCollectibles] = useState([]) + const [queriedCollectibles, setQueriedCollectibles] = useState([]) + const [isFetchingQueriedCollectibles, setIsFetchingQueriedCollectibles] = useState(false) + + const [isAddingCollectibleManually, setIsAddingCollectibleManually] = useState(false) + const [manualCollectibleInfo, setManualCollectibleInfo] = useState() + + const [queryCollectibleTokenIdsMap, setQueryCollectibleTokenIdsMap] = useState>({}) const fileInputRef = useRef(null) - useEffect(() => { - console.log(filteredCollectionList) - }, [filteredCollectionList]) + const selectOptions = mainnetNetworks + .filter(network => !network.disabled) + .map(network => ({ + label: ( + + + {network.title} + + ), + value: network.chainId.toString() + })) useEffect(() => { const fetchCollectibleList = async () => { @@ -68,35 +85,73 @@ export default function ImportCollectible({ onClose }: { onClose: () => void }) } } + const fetchCollectibleInfo = async () => { + if (selectedNetwork && contractType && collectibleManualAddress && collectibleManualTokenId) { + collectibleStore + .getCollectibleInfo({ + chainId: selectedNetwork.chainId, + address: collectibleManualAddress, + tokenId: collectibleManualTokenId, + contractType + }) + .then(response => { + setManualCollectibleInfo(response) + }) + } + } + fetchCollectibleList() + fetchCollectibleInfo() + }, [selectedNetwork, contractType, collectibleManualAddress, collectibleManualTokenId]) - if (selectedNetwork && collectibleManualAddress && collectibleManualTokenId && contractType) { - collectibleStore - .getCollectibleInfo({ - chainId: selectedNetwork.chainId, - address: collectibleManualAddress, - tokenId: collectibleManualTokenId, - contractType - }) - .then(response => { - setCollectibleInfo(response) - }) - } else { - setCollectibleInfo(undefined) + useEffect(() => { + if (selectedNetwork && contractType && selectedCollection) { } - }, [selectedNetwork, collectibleManualAddress, collectibleManualTokenId, contractType]) + }, [selectedNetwork, contractType, selectedCollection, selectedCollectibles]) - const selectOptions = mainnetNetworks - .filter(network => !network.disabled) - .map(network => ({ - label: ( - - - {network.title} - - ), - value: network.chainId.toString() - })) + useEffect(() => { + const fetchQueriedCollectibles = async () => { + setQueriedCollectibles([]) + if (!queryCollectibleTokenIdsMap[selectedCollection.address]) return + + setIsFetchingQueriedCollectibles(true) + const tokenIds = queryCollectibleTokenIdsMap[selectedCollection.address].split(',').map(Number) + + for (const tokenId of tokenIds) { + try { + const collectibleInfo = await collectibleStore.getCollectibleInfo({ + chainId: selectedNetwork.chainId, + contractType, + address: selectedCollection.address, + tokenId: tokenId + }) + + if (collectibleInfo.isOwner) { + setQueriedCollectibles(prev => [ + ...prev, + { + collectibleInfo, + chainId: selectedNetwork.chainId, + contractType, + address: selectedCollection.address, + tokenId + } + ]) + } + } catch (error) { + console.error(error) + continue + } + } + setIsFetchingQueriedCollectibles(false) + } + + fetchQueriedCollectibles() + }, [queryCollectibleTokenIdsMap]) + + useEffect(() => { + console.log(selectedCollectibles) + }, [selectedCollectibles]) useEffect(() => { const fetchCollectionList = async () => { @@ -124,54 +179,62 @@ export default function ImportCollectible({ onClose }: { onClose: () => void }) ) }, [collectionList, collectionListFilter]) - const toggleSelectCollectible = async (collectionAddress: string) => { - const isSelected = selectedCollection.some(collection => collection.address === collectionAddress) - if (isSelected) { - setSelectedCollection( - selectedCollection.filter(collectible => collectible.address !== collectionAddress) - ) - } else { - setSelectedCollection([...selectedCollection, { address: collectionAddress, info: undefined }]) - } - } - const handleAdd = async () => { - if ( - selectedNetwork && - collectibleManualAddress && - collectibleInfo && - collectibleManualTokenId && - contractType - ) { - await collectibleStore.addCollectible({ - collectibleInfoParams: { - chainId: selectedNetwork.chainId, - address: collectibleManualAddress, - tokenId: collectibleManualTokenId, - contractType - }, - collectibleInfoResponse: collectibleInfo - }) - setIsAddingCollectible(false) + try { + if ( + selectedNetwork && + contractType && + collectibleManualAddress && + collectibleManualTokenId && + manualCollectibleInfo + ) { + await collectibleStore.addCollectible({ + collectibleInfoParams: { + chainId: selectedNetwork.chainId, + address: collectibleManualAddress, + tokenId: collectibleManualTokenId, + contractType + }, + collectibleInfoResponse: manualCollectibleInfo + }) + } + + if (selectedCollectibles.length > 0) { + selectedCollectibles.map(async collectible => { + await collectibleStore.addCollectible({ + collectibleInfoParams: { + chainId: collectible.chainId, + address: collectible.address, + tokenId: collectible.tokenId, + contractType: collectible.contractType + }, + collectibleInfoResponse: collectible.collectibleInfo + }) + }) + } + + setIsAddingCollection(false) toast({ variant: 'success', - title: - contractType === CollectibleContractTypeValues.ERC721 - ? 'ERC721 collectible added successfully' - : 'ERC1155 collectible added successfully', + title: `Collectible${selectedCollectibles.length + (collectibleManualAddress ? 1 : 0) > 1 ? 's' : ''} added successfully`, description: "You'll be able to see this collectible on your browser as long as you don't clear your cache." }) resetInputs() onClose() + } catch (error) { + console.error(error) + toast({ + variant: 'error', + title: 'One or more collectibles failed to add', + description: 'Please try again.' + }) } } const resetInputs = () => { setCollectibleManualAddress(undefined) - setSelectedNetwork(undefined) setCollectibleManualTokenId(undefined) - setContractType(undefined) } const handleFileChange = async (event: ChangeEvent) => { @@ -219,326 +282,226 @@ export default function ImportCollectible({ onClose }: { onClose: () => void }) fileInputRef.current?.click() } - return ( - // - // - // - // Import ERC721 or ERC1155 Collectibles - // - - // - // - // - // Collectible Network - // - - // setContractType(value as CollectibleContractType)} - // /> - // - - // - // - // Token - // - - // {}} - // width="fit" - // cursor="pointer" - // paddingBottom="0.5" - // opacity={{ base: '100', hover: '80' }} - // > - // - // Import external token list - // - // - - // setSelectedNetwork(networks.find(n => n.chainId === Number(value)))} + const showCollectible = (collectible: any, i: number) => { + return ( + { + toggleSelectCollectible(collectible) + }} + borderRadius="sm" + padding="3" + gap="4" + > + + + {collectible.collectibleInfo.name} + + + + Balance: + + + {collectible.contractType === CollectibleContractTypeValues.ERC721 + ? 1 + : ethers.formatUnits( + collectible.collectibleInfo.balance as BigNumberish, + collectible.collectibleInfo.decimals ?? 0 + )} + + c.address.includes(collectible.address)).length || 0) > 0 + } /> + + ) + } - - - setContractType(CollectibleContractTypeValues.ERC721)}> - - ERC721 - - {contractType === CollectibleContractTypeValues.ERC721 && ( - - )} - - - setContractType(CollectibleContractTypeValues.ERC1155)}> - - ERC1155 - - {contractType === CollectibleContractTypeValues.ERC1155 && ( - - )} - + return ( + + {selectedCollection ? ( + + +