Explorer Metaplex NFTs: Adding External URL Button (#21095)
* Metaplex NFTs: Added external URL button to explorer - moved fetching MetadataJSON from URI to MetaplexNFTHeader - checked if external_url exists in MetadataJSON - rendered a button to link to the external url if it exists * cleanup unused import * fixed code formatting * Changed NFT website button to overview section field and Moved NFT URI fetch logic to accounts provider. * removed unused CSS * Update TokenAccountSection.tsx * fixed formatting and 1 other error
This commit is contained in:
parent
cd7331c6be
commit
ca716c400a
|
@ -15,10 +15,11 @@ export function NFTHeader({
|
|||
address: string;
|
||||
}) {
|
||||
const metadata = nftData.metadata;
|
||||
const data = nftData.json;
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-auto ms-2 d-flex align-items-center">
|
||||
<ArtContent metadata={metadata} pubkey={address} />
|
||||
<ArtContent metadata={metadata} pubkey={address} data={data} />
|
||||
</div>
|
||||
<div className="col mb-3 ms-0.5 mt-3">
|
||||
{<h6 className="header-pretitle ms-1">Metaplex NFT</h6>}
|
||||
|
|
|
@ -361,6 +361,21 @@ function NonFungibleTokenMintAccountCard({
|
|||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{nftData?.json && nftData.json.external_url && (
|
||||
<tr>
|
||||
<td>Website</td>
|
||||
<td className="text-lg-end">
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href={nftData.json.external_url}
|
||||
>
|
||||
{nftData.json.external_url}
|
||||
<span className="fe fe-external-link ms-2"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{nftData?.metadata.data && (
|
||||
<tr>
|
||||
<td>Seller Fee</td>
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from "@metaplex/js";
|
||||
import ContentLoader from "react-content-loader";
|
||||
import ErrorLogo from "img/logos-solana/dark-solana-logo.svg";
|
||||
import { getLast, pubkeyToString } from "utils";
|
||||
import { getLast } from "utils";
|
||||
|
||||
const MAX_TIME_LOADING_IMAGE = 5000; /* 5 seconds */
|
||||
|
||||
|
@ -242,6 +242,7 @@ export const ArtContent = ({
|
|||
uri,
|
||||
animationURL,
|
||||
files,
|
||||
data,
|
||||
}: {
|
||||
metadata: programs.metadata.MetadataData;
|
||||
category?: MetaDataJsonCategory;
|
||||
|
@ -250,11 +251,8 @@ export const ArtContent = ({
|
|||
uri?: string;
|
||||
animationURL?: string;
|
||||
files?: (MetadataJsonFile | string)[];
|
||||
data: MetadataJson | undefined;
|
||||
}) => {
|
||||
const id = pubkeyToString(pubkey);
|
||||
|
||||
const { data } = useExtendedArt(id, metadata);
|
||||
|
||||
if (pubkey && data) {
|
||||
uri = data.image;
|
||||
animationURL = data.animation_url;
|
||||
|
@ -357,56 +355,3 @@ export const useCachedImage = (uri: string) => {
|
|||
|
||||
return { cachedBlob };
|
||||
};
|
||||
|
||||
export const useExtendedArt = (
|
||||
id: string,
|
||||
metadata: programs.metadata.MetadataData
|
||||
) => {
|
||||
const [data, setData] = useState<MetadataJson>();
|
||||
|
||||
useEffect(() => {
|
||||
if (id && !data) {
|
||||
if (metadata.data.uri) {
|
||||
const uri = metadata.data.uri;
|
||||
|
||||
const processJson = (extended: any) => {
|
||||
if (!extended || extended?.properties?.files?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (extended?.image) {
|
||||
extended.image = extended.image.startsWith("http")
|
||||
? extended.image
|
||||
: `${metadata.data.uri}/${extended.image}`;
|
||||
}
|
||||
|
||||
return extended;
|
||||
};
|
||||
|
||||
try {
|
||||
fetch(uri)
|
||||
.then(async (_) => {
|
||||
try {
|
||||
const data = await _.json();
|
||||
try {
|
||||
localStorage.setItem(uri, JSON.stringify(data));
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
setData(processJson(data));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
return undefined;
|
||||
});
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [id, data, setData, metadata.data.uri]);
|
||||
|
||||
return { data };
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { pubkeyToString } from "utils";
|
||||
import { PublicKey, Connection, StakeActivationData } from "@solana/web3.js";
|
||||
import { useCluster, Cluster } from "../cluster";
|
||||
import { HistoryProvider } from "./history";
|
||||
|
@ -25,7 +26,7 @@ import {
|
|||
UpgradeableLoaderAccount,
|
||||
} from "validators/accounts/upgradeable-program";
|
||||
import { RewardsProvider } from "./rewards";
|
||||
import { programs } from "@metaplex/js";
|
||||
import { programs, MetadataJson } from "@metaplex/js";
|
||||
import getEditionInfo, { EditionInfo } from "./utils/getEditionInfo";
|
||||
export { useAccountHistory } from "./history";
|
||||
|
||||
|
@ -45,6 +46,7 @@ export type UpgradeableLoaderAccountData = {
|
|||
|
||||
export type NFTData = {
|
||||
metadata: programs.metadata.MetadataData;
|
||||
json: MetadataJson | undefined;
|
||||
editionInfo: EditionInfo;
|
||||
};
|
||||
|
||||
|
@ -252,8 +254,16 @@ async function fetchAccountInfo(
|
|||
metadata,
|
||||
connection
|
||||
);
|
||||
|
||||
nftData = { metadata: metadata.data, editionInfo };
|
||||
const id = pubkeyToString(pubkey);
|
||||
const metadataJSON = await getMetaDataJSON(
|
||||
id,
|
||||
metadata.data
|
||||
);
|
||||
nftData = {
|
||||
metadata: metadata.data,
|
||||
json: metadataJSON,
|
||||
editionInfo,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
@ -298,6 +308,53 @@ async function fetchAccountInfo(
|
|||
});
|
||||
}
|
||||
|
||||
const getMetaDataJSON = async (
|
||||
id: string,
|
||||
metadata: programs.metadata.MetadataData
|
||||
): Promise<MetadataJson | undefined> => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const uri = metadata.data.uri;
|
||||
if (!uri) return resolve(undefined);
|
||||
|
||||
const processJson = (extended: any) => {
|
||||
if (!extended || extended?.properties?.files?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (extended?.image) {
|
||||
extended.image = extended.image.startsWith("http")
|
||||
? extended.image
|
||||
: `${metadata.data.uri}/${extended.image}`;
|
||||
}
|
||||
|
||||
return extended;
|
||||
};
|
||||
|
||||
try {
|
||||
fetch(uri)
|
||||
.then(async (_) => {
|
||||
try {
|
||||
const data = await _.json();
|
||||
try {
|
||||
localStorage.setItem(uri, JSON.stringify(data));
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
resolve(processJson(data));
|
||||
} catch {
|
||||
resolve(undefined);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
resolve(undefined);
|
||||
});
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export function useAccounts() {
|
||||
const context = React.useContext(StateContext);
|
||||
if (!context) {
|
||||
|
|
Loading…
Reference in New Issue