From f559e3181eccc0c47f7fd6781d861937ae4eb37c Mon Sep 17 00:00:00 2001 From: Joe Le <55604805+nguyenlejoe@users.noreply.github.com> Date: Mon, 4 Dec 2023 21:25:49 -0800 Subject: [PATCH] Multchain NFT Wallet Token List (#20) --- README.md | 8 +- .../NFTWalletTokenListView.stories.tsx | 18 +- .../NFTWalletTokenListView.tsx | 163 ++++++++++++------ .../TokenBalancesListView.tsx | 1 - src/tailwind-input.css | 2 +- src/utils/types/organisms.types.ts | 2 +- 6 files changed, 139 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index d99b6185..e43a5526 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,13 @@ Be sure to secure your key to prevent unauthorized use in the Covalent platform ``` diff --git a/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.stories.tsx b/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.stories.tsx index b2009ec0..fd835f8f 100644 --- a/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.stories.tsx +++ b/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.stories.tsx @@ -12,7 +12,23 @@ export default meta; export const NFTWalletTokenList: Story = { args: { - chain_name: "eth-mainnet", + chain_names: [ + "eth-mainnet", + ], address: "0x1ae705a28f1cca0363b5d709159220aa2fe551de", }, }; + +export const MultiChainNFTWalletTokenList: Story = { + args: { + chain_names: [ + "eth-mainnet", + "matic-mainnet", + "bsc-mainnet", + "avalanche-mainnet", + "optimism-mainnet", + ], + address: "0x1ae705a28f1cca0363b5d709159220aa2fe551de", + } +}; + diff --git a/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx b/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx index f484d5a0..57c6c351 100644 --- a/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx +++ b/src/components/Organisms/NFTs/NFTWalletTokenListView/NFTWalletTokenListView.tsx @@ -1,8 +1,10 @@ import { GRK_SIZES } from "@/utils/constants/shared.constants"; import { type Option, Some, None } from "@/utils/option"; +import type { + ChainItem} from "@covalenthq/client-sdk"; import { prettifyCurrency, - type NftTokenContractBalanceItem, + type NftTokenContractBalanceItem } from "@covalenthq/client-sdk"; import { useEffect, useState } from "react"; import { @@ -16,35 +18,70 @@ import { AccountCardView } from "@/components/Molecules/AccountCardView/AccountC import { Skeleton } from "@/components/ui/skeleton"; import { useGoldrush } from "@/utils/store/Goldrush"; import { type NFTWalletTokenListViewProps } from "@/utils/types/organisms.types"; +import { TokenAvatar } from "@/components/Atoms/TokenAvatar/TokenAvatar"; export const NFTWalletTokenListView: React.FC = ({ - chain_name, + chain_names, address, }) => { const [maybeResult, setResult] = useState>(None); const { covalentClient } = useGoldrush(); const [error, setError] = useState({ error: false, error_message: "" }); + const [allChains, setAllChains] = useState>(None); - useEffect(() => { - (async () => { + const handleAllChains = async () => { + const allChainsResp = await covalentClient.BaseService.getAllChains(); + setAllChains(new Some(allChainsResp.data.items)); + }; + + const handleNftsToken = async () => { + const promises = chain_names.map(async (chain) => { + const allowedCacheChains = [ + 'bsc-mainnet', + 'eth-mainnet', + 'bsc-testnet', + 'eth-sepolia', + 'gnosis-mainnet', + 'gnosis-testnet', + 'matic-mainnet', + 'matic-mumbai' + ]; + const cache = !allowedCacheChains.includes(chain); let response; try { - response = await covalentClient.NftService.getNftsForAddress( - chain_name, - address.trim() - ); + response = + await covalentClient.NftService.getNftsForAddress( + chain, + address.trim(), + { + withUncached: cache + } + ); + setError({ error: false, error_message: "" }); - setResult(new Some(response.data.items)); - } catch (exception) { - setResult(new Some([])); + return response.data.items.map((o) => { + return { ...o, chain }; + }); + } catch (error) { + console.error(`Error fetching balances for ${chain}:`, error); setError({ error: response ? response.error : false, error_message: response ? response.error_message : "", }); + return []; } - })(); - }, [chain_name, address]); + }); + + const results = await Promise.all(promises); + setResult(new Some(results.flat())); + }; + + useEffect(() => { + handleAllChains(); + handleNftsToken(); + }, [chain_names, address]); + return maybeResult.match({ None: () => <>Loading, @@ -55,50 +92,76 @@ export const NFTWalletTokenListView: React.FC = ({ } else if (!error.error && result.length === 0) { body = <>No results; } else if (result.length > 0) { - body = result.map((items) => { + body = result.map((items: any) => { return flatMap(items.nft_data, (it) => { - return ( - - - { - e.currentTarget.classList.remove( - "object-cover" - ); - e.currentTarget.classList.add( - "p-2" - ); - e.currentTarget.src = - "https://www.datocms-assets.com/86369/1685489960-nft.svg"; - }} - /> - -
- - {" "} - {items.contract_name} - - - #{it.token_id?.toString()} - -
- - Est. Value - -

{items.pretty_floor_price_quote ? items.pretty_floor_price_quote : -}

-
-
-
- ); + return allChains.match({ + None: () => <>, + Some: (chains) => { + const chain: ChainItem = chains.filter( + (o) => o.name === items.chain + )[0]; + const chainColor = chain.color_theme.hex; + const isDarkMode = document.documentElement.classList.contains("dark"); + return ( + + + { + e.currentTarget.classList.remove( + "object-cover" + ); + e.currentTarget.classList.add( + "p-2" + ); + e.currentTarget.src = + "https://www.datocms-assets.com/86369/1685489960-nft.svg"; + }} + /> +
+ +
+ +
+
+ + {" "} + {items.contract_name} + + + #{it.token_id?.toString()} + +
+ + Est. Value + +

{items.pretty_floor_price_quote ? items.pretty_floor_price_quote : -}

+
+
+
+ ); + } + }); + }); }); } return (
-
+
diff --git a/src/components/Organisms/TokenBalances/TokenBalancesListView/TokenBalancesListView.tsx b/src/components/Organisms/TokenBalances/TokenBalancesListView/TokenBalancesListView.tsx index 4f1a289e..321a5ccb 100644 --- a/src/components/Organisms/TokenBalances/TokenBalancesListView/TokenBalancesListView.tsx +++ b/src/components/Organisms/TokenBalances/TokenBalancesListView/TokenBalancesListView.tsx @@ -66,7 +66,6 @@ export const TokenBalancesListView: React.FC = ({ const { covalentClient } = useGoldrush(); const [error, setError] = useState({ error: false, error_message: "" }); const [allChains, setAllChains] = useState>(None); - const [filterResult, setFilterResult] = useState>(None); const [windowWidth, setWindowWidth] = useState(0); diff --git a/src/tailwind-input.css b/src/tailwind-input.css index e8c89b22..31fef934 100644 --- a/src/tailwind-input.css +++ b/src/tailwind-input.css @@ -109,7 +109,7 @@ @apply bg-slate-900; @apply !text-white; } -:root.dark > body * { +:root.dark > body *:not(.tokenAvatar) { @apply !border-slate-700; } diff --git a/src/utils/types/organisms.types.ts b/src/utils/types/organisms.types.ts index 4dbfd9e6..e6e33f24 100644 --- a/src/utils/types/organisms.types.ts +++ b/src/utils/types/organisms.types.ts @@ -18,7 +18,7 @@ export interface NFTWalletCollectionViewProps { } export interface NFTWalletTokenListViewProps { - chain_name: Chain; + chain_names: Chain[]; address: string; }