feat(explorer): add attributes tab for metaplex nfts (#24580)
* feat(nfts): add attributes tab * fix: filter attributes to keep objects matching schema * chore: rename component and format * fix: support attribute value if it's a number
This commit is contained in:
parent
a22101489c
commit
2ae911ee55
|
@ -0,0 +1,87 @@
|
|||
import React from "react";
|
||||
import { NFTData } from "providers/accounts";
|
||||
import { LoadingCard } from "components/common/LoadingCard";
|
||||
import { ErrorCard } from "components/common/ErrorCard";
|
||||
|
||||
interface Attribute {
|
||||
trait_type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export function MetaplexNFTAttributesCard({ nftData }: { nftData: NFTData }) {
|
||||
const [attributes, setAttributes] = React.useState<Attribute[]>([]);
|
||||
const [status, setStatus] = React.useState<"loading" | "success" | "error">(
|
||||
"loading"
|
||||
);
|
||||
|
||||
async function fetchMetadataAttributes() {
|
||||
try {
|
||||
const response = await fetch(nftData.metadata.data.uri);
|
||||
const metadata = await response.json();
|
||||
|
||||
// Verify if the attributes value is an array
|
||||
if (Array.isArray(metadata.attributes)) {
|
||||
// Filter attributes to keep objects matching schema
|
||||
const filteredAttributes = metadata.attributes.filter(
|
||||
(attribute: any) => {
|
||||
return (
|
||||
typeof attribute === "object" &&
|
||||
typeof attribute.trait_type === "string" &&
|
||||
(typeof attribute.value === "string" ||
|
||||
typeof attribute.value === "number")
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
setAttributes(filteredAttributes);
|
||||
setStatus("success");
|
||||
} else {
|
||||
throw new Error("Attributes is not an array");
|
||||
}
|
||||
} catch (error) {
|
||||
setStatus("error");
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchMetadataAttributes();
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
if (status === "loading") {
|
||||
return <LoadingCard />;
|
||||
}
|
||||
|
||||
if (status === "error") {
|
||||
return <ErrorCard text="Failed to fetch attributes" />;
|
||||
}
|
||||
|
||||
const attributesList: React.ReactNode[] = attributes.map(
|
||||
({ trait_type, value }) => {
|
||||
return (
|
||||
<tr key={`${trait_type}:${value}`}>
|
||||
<td>{trait_type}</td>
|
||||
<td>{value}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
<h3 className="card-header-title">Attributes</h3>
|
||||
</div>
|
||||
<div className="table-responsive mb-0">
|
||||
<table className="table table-sm table-nowrap card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-muted w-1">Trait type</th>
|
||||
<th className="text-muted w-1">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="list">{attributesList}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -36,6 +36,7 @@ import { TokenTransfersCard } from "components/account/history/TokenTransfersCar
|
|||
import { TokenInstructionsCard } from "components/account/history/TokenInstructionsCard";
|
||||
import { RewardsCard } from "components/account/RewardsCard";
|
||||
import { MetaplexMetadataCard } from "components/account/MetaplexMetadataCard";
|
||||
import { MetaplexNFTAttributesCard } from "components/account/MetaplexNFTAttributesCard";
|
||||
import { NFTHeader } from "components/account/MetaplexNFTHeader";
|
||||
import { DomainsCard } from "components/account/DomainsCard";
|
||||
import isMetaplexNFT from "providers/accounts/utils/isMetaplexNFT";
|
||||
|
@ -70,6 +71,11 @@ const TABS_LOOKUP: { [id: string]: Tab[] } = {
|
|||
title: "Metadata",
|
||||
path: "/metadata",
|
||||
},
|
||||
{
|
||||
slug: "attributes",
|
||||
title: "Attributes",
|
||||
path: "/attributes",
|
||||
},
|
||||
],
|
||||
stake: [
|
||||
{
|
||||
|
@ -360,6 +366,7 @@ export type MoreTabs =
|
|||
| "instructions"
|
||||
| "rewards"
|
||||
| "metadata"
|
||||
| "attributes"
|
||||
| "domains"
|
||||
| "security"
|
||||
| "anchor-program"
|
||||
|
@ -420,6 +427,11 @@ function MoreSection({
|
|||
nftData={(account.details?.data as TokenProgramData).nftData!}
|
||||
/>
|
||||
)}
|
||||
{tab === "attributes" && (
|
||||
<MetaplexNFTAttributesCard
|
||||
nftData={(account.details?.data as TokenProgramData).nftData!}
|
||||
/>
|
||||
)}
|
||||
{tab === "domains" && <DomainsCard pubkey={pubkey} />}
|
||||
{tab === "security" && data?.program === "bpf-upgradeable-loader" && (
|
||||
<SecurityCard data={data} />
|
||||
|
|
Loading…
Reference in New Issue