Skip to content

Commit

Permalink
Fix the way NFTs are displayed
Browse files Browse the repository at this point in the history
- Display NFTs that don't embed a json
- Display NFTs without images
- Do not display NFTs without URIs
  • Loading branch information
ThibautBremand committed Jun 28, 2023
1 parent 26f35e1 commit 7246b5d
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 40 deletions.
5 changes: 3 additions & 2 deletions packages/constants/src/xrpl/nft.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
57 changes: 37 additions & 20 deletions packages/extension/src/components/molecules/NFTCard/NFTCard.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
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;
}

export const NFTCard: FC<NFTCardProps> = ({ NFT }) => {
const { getNFTData } = useContext(LedgerContext);
const [nftData, setNftData] = useState<NFTData | null>(null);
const [NFTData, setNFTData] = useState<NFTData | null>(null);
const [loading, setLoading] = useState<boolean>(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 (
<Paper
elevation={5}
Expand All @@ -55,34 +60,46 @@ export const NFTCard: FC<NFTCardProps> = ({ NFT }) => {
>
{loading ? (
<CircularProgress data-testid="progressbar" />
) : (
) : NFTData?.image ? (
<LazyLoadImage
alt="nft"
height={150}
style={{ borderRadius: '4px', boxShadow: '4px 4px 0px black' }}
beforeLoad={() => <CircularProgress />}
effect="blur"
src={nftData?.image}
src={NFTData?.image}
width={150}
/>
) : (
<GemWallet />
)}
{nftData && (
<>
{NFTData ? (
<Tooltip title={NFTData.NFTokenID}>
<div
style={{ fontSize: '16px', color: 'white', marginTop: '10px' }}
data-testid="nft_name"
style={{ fontSize: '14px', color: 'grey', marginTop: '10px', cursor: 'pointer' }}
onClick={() => handleTokenIdClick(NFTData.NFTokenID)}
>
{nftData.name}
</div>
<div style={{ fontSize: '14px', color: 'grey', marginTop: '10px' }}>
{nftData.description}
{NFTData.NFTokenID.substring(0, 10) + '...'}
</div>
</>
)}
</Tooltip>
) : null}
{NFTData?.name ? (
<div
style={{ fontSize: '16px', color: 'white', marginTop: '10px' }}
data-testid="nft_name"
>
{NFTData.name}
</div>
) : null}
{NFTData ? (
<div style={{ fontSize: '14px', color: 'grey', marginTop: '10px' }}>
{NFTData.description}
</div>
) : null}
<Button
variant="outlined"
style={{ marginTop: '10px', fontSize: '14px', gap: '10px' }}
onClick={handleViewNftClick}
onClick={handleViewNFTClick}
>
View <OpenInNewOutlined style={{ fontSize: '16px' }} />
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,14 +27,16 @@ export const NFTListing: FC<NFTListingProps> = ({ loading, account_nfts, onLoadM
<InfiniteScroll
dataLength={account_nfts.length}
next={onLoadMoreClick}
hasMore={true}
hasMore={account_nfts.length >= MAX_FETCHED_NFTS}
height={450}
loader={<h4>Loading...</h4>}
>
<List dense>
{account_nfts.map((nft) => (
<NFTCard key={nft.NFTokenID} NFT={nft} />
))}
{account_nfts
.filter((NFT) => NFT.URI) // Do not display NFTs without URI
.map((NFT) => (
<NFTCard key={NFT.NFTokenID} NFT={NFT} />
))}
</List>
</InfiniteScroll>
);
Expand Down
4 changes: 3 additions & 1 deletion packages/extension/src/components/pages/NFTs/NFTs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -24,7 +26,7 @@ export const NFTs: FC = () => {
const fetchNFTs = async () => {
try {
const payload = {
limit: 20,
limit: MAX_FETCHED_NFTS,
marker: NFTs.marker ?? undefined
};

Expand Down
38 changes: 26 additions & 12 deletions packages/extension/src/contexts/LedgerContext/LedgerContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -736,25 +736,39 @@ 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) {
throw new Error('URI is empty');
}

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;
Expand Down

0 comments on commit 7246b5d

Please sign in to comment.