Skip to content

Commit daafb02

Browse files
authored
Add testnet support to NFT and NFTGallery (#270)
1 parent 2bfb4cc commit daafb02

File tree

6 files changed

+75
-7
lines changed

6 files changed

+75
-7
lines changed

.changeset/tidy-dancers-draw.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@web3-ui/components': minor
3+
---
4+
5+
The NFT and NFTGallery components now support testnets (rinkeby only for now)! You can pass in `isTestnet` to the components if you want to fetch the NFT data from the testnet API. 🎉
6+
7+
```tsx
8+
<NFT
9+
contractAddress="0xd067fae3311a5daefe21b81ec17224c7b2652ca6"
10+
tokenId="20"
11+
isTestnet
12+
/>
13+
14+
<NFTGallery
15+
address="0x0ED6Cec17F860fb54E21D154b49DAEFd9Ca04106"
16+
gridWidth={2}
17+
isTestnet
18+
/>
19+
```

packages/components/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ You need to pass in a `provider` prop if you want to use ENS names.
6767

6868
### NFT
6969

70-
The NFT component takes in the contract address and the token ID of an NFT and displays it as a card.
70+
The NFT component takes in the contract address and the token ID of an NFT and displays it as a card. You can also pass in `isTestnet` to fetch the NFT data from the testnet API (only Rinkeby for now).
7171

7272
```tsx
7373
<NFT contractAddress="0xxxxx0x0x0x0x0x" tokenId={30} size="md" />
@@ -79,7 +79,7 @@ The `size` prop is optional.
7979

8080
### NFTGallery
8181

82-
The NFTGallery component renders a grid of all the NFTs owned by an account. It accepts ENS names too.
82+
The NFTGallery component renders a grid of all the NFTs owned by an account. It accepts ENS names too. You can also pass in `isTestnet` to fetch the NFT data from the testnet API (only Rinkeby for now).
8383

8484
```tsx
8585
<NFTGallery address="vitalik.eth" web3Provider={provider} gridWidth={4} />

packages/components/src/components/NFT/NFT.stories.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,12 @@ export const Big = () => (
4242
/>
4343
);
4444

45+
export const Rinkeby = () => (
46+
<NFT
47+
contractAddress="0xd067fae3311a5daefe21b81ec17224c7b2652ca6"
48+
tokenId="20"
49+
isTestnet
50+
/>
51+
);
52+
4553
export const Error = () => <NFT contractAddress="abcd" tokenId="1" />;

packages/components/src/components/NFT/NFT.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useEffect, useRef } from 'react';
1+
import React, { useCallback, useEffect, useRef, useState } from 'react';
22
import {
33
Box,
44
Heading,
@@ -25,6 +25,10 @@ export interface NFTProps {
2525
* The size of the NFT card.
2626
*/
2727
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
28+
/**
29+
* Use testnet API (Rinkeby) instead of mainnet
30+
*/
31+
isTestnet?: boolean;
2832
}
2933

3034
export interface NFTData {
@@ -39,16 +43,27 @@ export interface NFTData {
3943
/**
4044
* Component to fetch and display NFT data
4145
*/
42-
export const NFT = ({ contractAddress, tokenId, size = 'xs' }: NFTProps) => {
46+
export const NFT = ({
47+
contractAddress,
48+
tokenId,
49+
size = 'xs',
50+
isTestnet = false,
51+
}: NFTProps) => {
4352
const _isMounted = useRef(true);
4453
const [nftData, setNftData] = React.useState<NFTData>();
4554
const [errorMessage, setErrorMessage] = React.useState<string>();
4655

4756
const fetchNFTData = useCallback(async () => {
57+
const apiSubDomain = isTestnet ? `rinkeby-api` : `api`;
58+
4859
try {
4960
const res = await fetch(
50-
`https://api.opensea.io/api/v1/asset/${contractAddress}/${tokenId}/`
61+
`https://${apiSubDomain}.opensea.io/api/v1/asset/${contractAddress}/${tokenId}/`
5162
);
63+
if (isTestnet)
64+
console.log(
65+
`⚠️ OpenSea currently only supports Rinkedby with testnets.`
66+
);
5267
if (!res.ok) {
5368
throw Error(
5469
`OpenSea request failed with status: ${res.status}. Make sure you are on mainnet.`
@@ -92,11 +107,14 @@ export const NFTCard = ({
92107
data,
93108
errorMessage = '',
94109
size,
110+
hideIfError = false,
95111
}: {
96112
data: NFTData | undefined | null;
97113
errorMessage?: string | undefined;
98114
size: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
115+
hideIfError?: boolean;
99116
}) => {
117+
const [error, setError] = useState(false);
100118
const name = data?.name;
101119
const imageUrl = data?.imageUrl;
102120
const assetContractName = data?.assetContractName;
@@ -105,7 +123,8 @@ export const NFTCard = ({
105123
const tokenId = data?.tokenId;
106124
const displayName = name || `${assetContractSymbol} #${tokenId}`;
107125

108-
if (errorMessage) {
126+
if (errorMessage || error || imageUrl === '(unknown)' || !imageUrl) {
127+
if (hideIfError) return null;
109128
return (
110129
<Alert status="error">
111130
<AlertIcon />
@@ -125,6 +144,7 @@ export const NFTCard = ({
125144
alt={displayName}
126145
borderRadius="lg"
127146
w={size}
147+
onError={() => setError(true)}
128148
/>
129149
<audio
130150
src={animationUrl}

packages/components/src/components/NFTGallery/NFTGallery.stories.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,12 @@ nftsOwnedByAnENS.parameters = {
3636
chromatic: { disableSnapshot: true },
3737
};
3838

39+
export const rinkeby = () => (
40+
<NFTGallery
41+
address="0x0ED6Cec17F860fb54E21D154b49DAEFd9Ca04106"
42+
gridWidth={2}
43+
isTestnet
44+
/>
45+
);
46+
3947
export const WithAnError = () => <NFTGallery address="bad_address" />;

packages/components/src/components/NFTGallery/NFTGallery.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export interface NFTGalleryProps {
1717
* A Web3Provider. Only needed if the address will be an ENS name.
1818
*/
1919
web3Provider?: ethers.providers.Web3Provider;
20+
/**
21+
* Use testnet api instead of mainnet
22+
*/
23+
isTestnet?: boolean;
2024
}
2125

2226
export interface OpenSeaAsset {
@@ -38,20 +42,28 @@ export const NFTGallery = ({
3842
address,
3943
gridWidth = 4,
4044
web3Provider,
45+
isTestnet = false,
4146
}: NFTGalleryProps) => {
4247
const [nfts, setNfts] = React.useState<OpenSeaAsset[]>([]);
4348
const [errorMessage, setErrorMessage] = React.useState(null);
4449

4550
useEffect(() => {
4651
async function exec() {
4752
let resolvedAddress: string | null = address;
53+
const apiSubDomain = isTestnet ? `rinkeby-api` : `api`;
54+
if (isTestnet)
55+
console.log(
56+
`⚠️ OpenSea currently only supports Rinkedby with testnets.`
57+
);
4858
if (address.endsWith('.eth')) {
4959
if (!web3Provider) {
5060
return console.error('Please provide a web3 provider');
5161
}
5262
resolvedAddress = await web3Provider.resolveName(address);
5363
}
54-
fetch(`https://api.opensea.io/api/v1/assets?owner=${resolvedAddress}`)
64+
fetch(
65+
`https://${apiSubDomain}.opensea.io/api/v1/assets?owner=${resolvedAddress}`
66+
)
5567
.then((res) => {
5668
if (!res.ok) {
5769
throw Error(
@@ -89,6 +101,7 @@ export const NFTGallery = ({
89101
assetContractSymbol: nft.asset_contract.symbol,
90102
}}
91103
size="xs"
104+
hideIfError
92105
/>
93106
))}
94107
</Grid>

0 commit comments

Comments
 (0)