diff --git a/packages/constants/src/xrpl/nft.types.ts b/packages/constants/src/xrpl/nft.types.ts index a031728dc..48042b493 100644 --- a/packages/constants/src/xrpl/nft.types.ts +++ b/packages/constants/src/xrpl/nft.types.ts @@ -13,8 +13,9 @@ export interface AccountNFTokenResponse { } export interface NFTData { - schema: string; - nftType: string; + NFTokenID: string; + NFType?: string; + schema?: string; name?: string; description?: string; image?: string; diff --git a/packages/extension/src/components/molecules/NFTCard/NFTCard.test.tsx b/packages/extension/src/components/molecules/NFTCard/NFTCard.test.tsx index 6de5e0faa..f3b546825 100644 --- a/packages/extension/src/components/molecules/NFTCard/NFTCard.test.tsx +++ b/packages/extension/src/components/molecules/NFTCard/NFTCard.test.tsx @@ -14,8 +14,9 @@ const mockNFT = { }; const mockNFTData = { + NFTokenID: 'fake', schema: 'ipfs://QmNpi8rcXEkohca8iXu7zysKKSJYqCvBJn3xJwga8jXqWU', - nftType: 'art.v0', + NFType: 'art.v0', name: "Ekiserrepe's Oniric Lo-Fi Rooms Vol.1 NFT #1", description: "Room #1 of Ekiserrepe's Oniric Lo-Fi Rooms Vol.1", image: 'ipfs://bafybeie6pmuddco552t4u7oc7anryqohuj6vl42ngct6ve3q4bjet5piam/1.png', diff --git a/packages/extension/src/components/molecules/NFTCard/NFTCard.tsx b/packages/extension/src/components/molecules/NFTCard/NFTCard.tsx index 95acef112..3d4a622a7 100644 --- a/packages/extension/src/components/molecules/NFTCard/NFTCard.tsx +++ b/packages/extension/src/components/molecules/NFTCard/NFTCard.tsx @@ -1,12 +1,13 @@ import { FC, useContext, useEffect, useState } from 'react'; import { OpenInNewOutlined } from '@mui/icons-material'; -import { Button, CircularProgress, ListItem, Paper } from '@mui/material'; +import { Button, CircularProgress, ListItem, Paper, Tooltip } from '@mui/material'; import { LazyLoadImage } from 'react-lazy-load-image-component'; import { AccountNFToken, NFTData } from '@gemwallet/constants'; import { LedgerContext } from '../../../contexts'; +import { GemWallet } from '../../atoms'; export interface NFTCardProps { NFT: AccountNFToken; @@ -14,28 +15,32 @@ export interface NFTCardProps { export const NFTCard: FC = ({ NFT }) => { const { getNFTData } = useContext(LedgerContext); - const [nftData, setNftData] = useState(null); + const [NFTData, setNFTData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { - const fetchNftImg = async () => { + const fetchNFTImg = async () => { try { setLoading(true); const nftData = await getNFTData({ NFT }); - setNftData(nftData); + setNFTData(nftData); } catch (error) { - setNftData(null); + setNFTData(null); } finally { setLoading(false); } }; - fetchNftImg(); + fetchNFTImg(); }, [getNFTData, NFT]); - const handleViewNftClick = () => { + const handleViewNFTClick = () => { window.open('someurl', '_blank'); // TODO: Add redirection url (Potential collaboration with NFT marketplace)? }; + const handleTokenIdClick = (tokenId: string) => { + navigator.clipboard.writeText(tokenId); + }; + return ( = ({ NFT }) => { > {loading ? ( - ) : ( + ) : NFTData?.image ? ( } effect="blur" - src={nftData?.image} + src={NFTData?.image} width={150} /> + ) : ( + )} - {nftData && ( - <> + {NFTData ? ( +
handleTokenIdClick(NFTData.NFTokenID)} > - {nftData.name} -
-
- {nftData.description} + {NFTData.NFTokenID.substring(0, 10) + '...'}
- - )} +
+ ) : null} + {NFTData?.name ? ( +
+ {NFTData.name} +
+ ) : null} + {NFTData ? ( +
+ {NFTData.description} +
+ ) : null} diff --git a/packages/extension/src/components/organisms/NFTListing/NFTListing.tsx b/packages/extension/src/components/organisms/NFTListing/NFTListing.tsx index 8815cd549..ce6f63c17 100644 --- a/packages/extension/src/components/organisms/NFTListing/NFTListing.tsx +++ b/packages/extension/src/components/organisms/NFTListing/NFTListing.tsx @@ -7,6 +7,7 @@ import { AccountNFTokenResponse } from '@gemwallet/constants'; import { InformationMessage } from '../../molecules'; import { NFTCard } from '../../molecules/NFTCard'; +import { MAX_FETCHED_NFTS } from '../../pages'; export interface NFTListingProps extends AccountNFTokenResponse { onLoadMoreClick: () => void; @@ -26,14 +27,16 @@ export const NFTListing: FC = ({ loading, account_nfts, onLoadM = MAX_FETCHED_NFTS} height={450} loader={

Loading...

} > - {account_nfts.map((nft) => ( - - ))} + {account_nfts + .filter((NFT) => NFT.URI) // Do not display NFTs without URI + .map((NFT) => ( + + ))}
); diff --git a/packages/extension/src/components/pages/NFTs/NFTs.tsx b/packages/extension/src/components/pages/NFTs/NFTs.tsx index 6f16ddf53..7b70817d4 100644 --- a/packages/extension/src/components/pages/NFTs/NFTs.tsx +++ b/packages/extension/src/components/pages/NFTs/NFTs.tsx @@ -6,6 +6,8 @@ import { LedgerContext } from '../../../contexts'; import { NFTListing } from '../../organisms/NFTListing'; import { PageWithHeader } from '../../templates'; +export const MAX_FETCHED_NFTS = 20; + const initalState = { account_nfts: [], marker: null, @@ -24,7 +26,7 @@ export const NFTs: FC = () => { const fetchNFTs = async () => { try { const payload = { - limit: 20, + limit: MAX_FETCHED_NFTS, marker: NFTs.marker ?? undefined }; diff --git a/packages/extension/src/contexts/LedgerContext/LedgerContext.tsx b/packages/extension/src/contexts/LedgerContext/LedgerContext.tsx index a0e5dd4b4..f74e6fd43 100644 --- a/packages/extension/src/contexts/LedgerContext/LedgerContext.tsx +++ b/packages/extension/src/contexts/LedgerContext/LedgerContext.tsx @@ -736,7 +736,7 @@ const LedgerProvider: FC = ({ children }) => { const getNFTData = useCallback(async ({ NFT }: NFTImageRequest) => { try { - const { URI } = NFT; + const { NFTokenID, URI } = NFT; let URL = URI ? await convertHexToString(String(URI)) : ''; if (URL.length === 0) { @@ -744,17 +744,31 @@ const LedgerProvider: FC = ({ children }) => { } URL = URL.replace('ipfs://', 'https://ipfs.io/ipfs/'); - const NFTData = await fetch(URL) - .then((res) => res.json()) - .catch(() => ({ - name: '-', - description: '-', - image: null - })); - NFTData.image = NFTData.image - ? NFTData.image.replace('ipfs://', 'https://ipfs.io/ipfs/') - : URL.replace('.json', '.png'); - return NFTData; + + if (URL.includes('.json')) { + // Parse the JSON, in order to display the NFT in the UI + const NFTData = await fetch(URL) + .then((res) => res.json()) + .catch(() => { + throw new Error('Error fetching NFT data'); + }); + + const image = NFTData.image + ? NFTData.image.replace('ipfs://', 'https://ipfs.io/ipfs/') + : URL.replace('.json', '.png'); + + return { + ...NFTData, + NFTokenID, + image + }; + } else { + // No JSON to parse, just display the raw NFT attributes + return { + NFTokenID, + description: URL + }; + } } catch (e) { Sentry.captureException(e); throw e;