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:
Thomas Pompon 2022-04-22 14:06:00 +02:00 committed by GitHub
parent a22101489c
commit 2ae911ee55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 0 deletions

View File

@ -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>
);
}

View File

@ -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} />