Skip to content

Commit b81fd41

Browse files
committed
updated to include erc721 and erc1155 for mutation functions
1 parent aab676f commit b81fd41

File tree

6 files changed

+331
-17
lines changed

6 files changed

+331
-17
lines changed

apps/dashboard/2

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
"use client";
2+
import {
3+
Accordion,
4+
AccordionContent,
5+
AccordionItem,
6+
AccordionTrigger,
7+
} from "@/components/ui/accordion";
8+
import { Alert, AlertTitle } from "@/components/ui/alert";
9+
import {
10+
Form,
11+
FormControl,
12+
FormField,
13+
FormItem,
14+
FormLabel,
15+
FormMessage,
16+
} from "@/components/ui/form";
17+
import { Input } from "@/components/ui/input";
18+
import { Textarea } from "@/components/ui/textarea";
19+
import { zodResolver } from "@hookform/resolvers/zod";
20+
import { useMutation } from "@tanstack/react-query";
21+
import { TransactionButton } from "components/buttons/TransactionButton";
22+
import { useTxNotifications } from "hooks/useTxNotifications";
23+
import { CircleAlertIcon } from "lucide-react";
24+
import { useCallback } from "react";
25+
import { useForm } from "react-hook-form";
26+
import { sendAndConfirmTransaction } from "thirdweb";
27+
import { BatchMetadataERC721, BatchMetadataERC1155 } from "thirdweb/modules";
28+
import { parseAttributes } from "utils/parseAttributes";
29+
import { z } from "zod";
30+
import { fileBufferOrStringSchema } from "../zod-schemas";
31+
import { ModuleCardUI, type ModuleCardUIProps } from "./module-card";
32+
import type { ModuleInstanceProps } from "./module-instance";
33+
import { AdvancedNFTMetadataFormGroup } from "./nft/AdvancedNFTMetadataFormGroup";
34+
import { NFTMediaFormGroup } from "./nft/NFTMediaFormGroup";
35+
import { PropertiesFormControl } from "./nft/PropertiesFormControl";
36+
37+
const uploadMetadataFormSchema = z.object({
38+
name: z.string().min(1),
39+
description: z.string().optional(),
40+
image: fileBufferOrStringSchema.optional(),
41+
animationUri: fileBufferOrStringSchema.optional(),
42+
external_url: fileBufferOrStringSchema.optional(),
43+
customImage: z.string().optional(),
44+
customAnimationUrl: z.string().optional(),
45+
background_color: z
46+
.string()
47+
.refine(
48+
(c) => {
49+
return /^#[0-9a-f]{6}$/i.test(c.toLowerCase());
50+
},
51+
{
52+
message: "Invalid Hex Color",
53+
},
54+
)
55+
.optional(),
56+
attributes: z.array(
57+
z.object({
58+
trait_type: z.string().min(1),
59+
value: z.string().min(1),
60+
}),
61+
),
62+
});
63+
64+
export type UploadMetadataFormValues = z.infer<typeof uploadMetadataFormSchema>;
65+
66+
function BatchMetadataModule(props: ModuleInstanceProps) {
67+
const { contract, ownerAccount } = props;
68+
69+
const isErc721 = props.contractInfo.name === "BatchMetadataERC721";
70+
71+
const uploadMetadata = useCallback(
72+
async (values: UploadMetadataFormValues) => {
73+
if (!ownerAccount) {
74+
throw new Error("Not an owner account");
75+
}
76+
77+
const nft = parseAttributes(values);
78+
const uploadMetadata = isErc721
79+
? BatchMetadataERC721.uploadMetadata
80+
: BatchMetadataERC1155.uploadMetadata;
81+
const uploadMetadataTx = uploadMetadata({
82+
contract,
83+
metadatas: [nft],
84+
});
85+
86+
await sendAndConfirmTransaction({
87+
account: ownerAccount,
88+
transaction: uploadMetadataTx,
89+
});
90+
},
91+
[contract, ownerAccount],
92+
);
93+
94+
return (
95+
<BatchMetadataModuleUI
96+
{...props}
97+
uploadMetadata={uploadMetadata}
98+
isOwnerAccount={!!ownerAccount}
99+
contractChainId={contract.chain.id}
100+
/>
101+
);
102+
}
103+
104+
export function BatchMetadataModuleUI(
105+
props: Omit<ModuleCardUIProps, "children" | "updateButton"> & {
106+
isOwnerAccount: boolean;
107+
uploadMetadata: (values: UploadMetadataFormValues) => Promise<void>;
108+
contractChainId: number;
109+
},
110+
) {
111+
return (
112+
<ModuleCardUI {...props}>
113+
<div className="flex flex-col gap-4">
114+
<Accordion type="single" collapsible className="-mx-1">
115+
{/* uploadMetadata */}
116+
<AccordionItem value="metadata" className="border-none">
117+
<AccordionTrigger className="border-border border-t px-1">
118+
Upload NFT Metadata
119+
</AccordionTrigger>
120+
<AccordionContent className="px-1">
121+
{props.isOwnerAccount && (
122+
<UploadMetadataNFTSection
123+
uploadMetadata={props.uploadMetadata}
124+
contractChainId={props.contractChainId}
125+
/>
126+
)}
127+
{!props.isOwnerAccount && (
128+
<Alert variant="info">
129+
<CircleAlertIcon className="size-5" />
130+
<AlertTitle>
131+
You don't have permission to upload metadata on this
132+
contract
133+
</AlertTitle>
134+
</Alert>
135+
)}
136+
</AccordionContent>
137+
</AccordionItem>
138+
139+
{/* batchMetadata */}
140+
<AccordionItem value="batch-metadata" className="border-none">
141+
<AccordionTrigger className="border-border border-t px-1">
142+
Batch Upload NFT Metadata
143+
</AccordionTrigger>
144+
<AccordionContent className="px-1">
145+
{props.isOwnerAccount && <BatchMetadataNFTSection />}
146+
{!props.isOwnerAccount && (
147+
<Alert variant="info">
148+
<CircleAlertIcon className="size-5" />
149+
<AlertTitle>
150+
You don't have permission to upload metadata on this
151+
contract
152+
</AlertTitle>
153+
</Alert>
154+
)}
155+
</AccordionContent>
156+
</AccordionItem>
157+
</Accordion>
158+
</div>
159+
</ModuleCardUI>
160+
);
161+
}
162+
163+
function UploadMetadataNFTSection(props: {
164+
uploadMetadata: (values: UploadMetadataFormValues) => Promise<void>;
165+
contractChainId: number;
166+
}) {
167+
const form = useForm<UploadMetadataFormValues>({
168+
resolver: zodResolver(uploadMetadataFormSchema),
169+
values: {
170+
name: "",
171+
attributes: [],
172+
},
173+
reValidateMode: "onChange",
174+
});
175+
176+
const uploadNotifications = useTxNotifications(
177+
"NFT metadata uploaded successfully",
178+
"Failed to uploadMetadata NFT metadata",
179+
);
180+
181+
const uploadMetadataMutation = useMutation({
182+
mutationFn: props.uploadMetadata,
183+
onSuccess: uploadNotifications.onSuccess,
184+
onError: uploadNotifications.onError,
185+
});
186+
187+
const onSubmit = async () => {
188+
uploadMetadataMutation.mutateAsync(form.getValues());
189+
};
190+
191+
return (
192+
<Form {...form}>
193+
<form onSubmit={form.handleSubmit(onSubmit)}>
194+
<div className="flex flex-col gap-6">
195+
<div className="flex flex-col gap-6 lg:flex-row">
196+
{/* Left */}
197+
<div className="shrink-0 lg:w-[300px]">
198+
<NFTMediaFormGroup form={form} previewMaxWidth="300px" />
199+
</div>
200+
201+
{/* Right */}
202+
<div className="flex grow flex-col gap-6">
203+
{/* name */}
204+
<FormField
205+
control={form.control}
206+
name="name"
207+
render={({ field }) => (
208+
<FormItem>
209+
<FormLabel>Name</FormLabel>
210+
<FormControl>
211+
<Input {...field} />
212+
</FormControl>
213+
214+
<FormMessage />
215+
</FormItem>
216+
)}
217+
/>
218+
219+
{/* Description */}
220+
<FormField
221+
control={form.control}
222+
name="description"
223+
render={({ field }) => (
224+
<FormItem>
225+
<FormLabel>Description</FormLabel>
226+
<FormControl>
227+
<Textarea {...field} />
228+
</FormControl>
229+
230+
<FormMessage />
231+
</FormItem>
232+
)}
233+
/>
234+
235+
<PropertiesFormControl form={form} />
236+
237+
{/* Advanced options */}
238+
<Accordion
239+
type="single"
240+
collapsible={
241+
!(
242+
form.formState.errors.background_color ||
243+
form.formState.errors.external_url
244+
)
245+
}
246+
>
247+
<AccordionItem
248+
value="advanced-options"
249+
className="-mx-1 border-y"
250+
>
251+
<AccordionTrigger className="px-1">
252+
Advanced Options
253+
</AccordionTrigger>
254+
<AccordionContent className="px-1">
255+
<AdvancedNFTMetadataFormGroup form={form} />
256+
</AccordionContent>
257+
</AccordionItem>
258+
</Accordion>
259+
</div>
260+
</div>
261+
262+
<div className="flex justify-end">
263+
<TransactionButton
264+
size="sm"
265+
className="min-w-24"
266+
disabled={uploadMetadataMutation.isPending}
267+
type="submit"
268+
isLoading={uploadMetadataMutation.isPending}
269+
txChainID={props.contractChainId}
270+
transactionCount={1}
271+
colorScheme="primary"
272+
>
273+
Upload
274+
</TransactionButton>
275+
</div>
276+
</div>
277+
</form>
278+
</Form>
279+
);
280+
}
281+
282+
function BatchMetadataNFTSection() {
283+
return (
284+
<Alert variant="info">
285+
<CircleAlertIcon className="size-5" />
286+
<AlertTitle>Coming soon!</AlertTitle>
287+
</Alert>
288+
);
289+
}
290+
291+
export default BatchMetadataModule;

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { CircleAlertIcon } from "lucide-react";
2424
import { useCallback } from "react";
2525
import { useForm } from "react-hook-form";
2626
import { sendAndConfirmTransaction } from "thirdweb";
27-
import { BatchMetadataERC721 } from "thirdweb/modules";
27+
import { BatchMetadataERC721, BatchMetadataERC1155 } from "thirdweb/modules";
2828
import { parseAttributes } from "utils/parseAttributes";
2929
import { z } from "zod";
3030
import { fileBufferOrStringSchema } from "../zod-schemas";
@@ -66,14 +66,19 @@ export type UploadMetadataFormValues = z.infer<typeof uploadMetadataFormSchema>;
6666
function BatchMetadataModule(props: ModuleInstanceProps) {
6767
const { contract, ownerAccount } = props;
6868

69+
const isErc721 = props.contractInfo.name === "BatchMetadataERC721";
70+
6971
const uploadMetadata = useCallback(
7072
async (values: UploadMetadataFormValues) => {
7173
if (!ownerAccount) {
7274
throw new Error("Not an owner account");
7375
}
7476

7577
const nft = parseAttributes(values);
76-
const uploadMetadataTx = BatchMetadataERC721.uploadMetadata({
78+
const uploadMetadata = isErc721
79+
? BatchMetadataERC721.uploadMetadata
80+
: BatchMetadataERC1155.uploadMetadata;
81+
const uploadMetadataTx = uploadMetadata({
7782
contract,
7883
metadatas: [nft],
7984
});
@@ -83,7 +88,7 @@ function BatchMetadataModule(props: ModuleInstanceProps) {
8388
transaction: uploadMetadataTx,
8489
});
8590
},
86-
[contract, ownerAccount],
91+
[contract, ownerAccount, isErc721],
8792
);
8893

8994
return (

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Claimable.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ function ClaimableModule(props: ModuleInstanceProps) {
190190
if (!ownerAccount) {
191191
throw new Error("Not an owner account");
192192
}
193-
const setSaleConfigTx = ClaimableERC721.setSaleConfig({
193+
const setSaleConfig = isErc721
194+
? ClaimableERC721.setSaleConfig
195+
: ClaimableERC1155.setSaleConfig;
196+
const setSaleConfigTx = setSaleConfig({
194197
contract: contract,
195198
primarySaleRecipient: values.primarySaleRecipient,
196199
});
@@ -200,7 +203,7 @@ function ClaimableModule(props: ModuleInstanceProps) {
200203
transaction: setSaleConfigTx,
201204
});
202205
},
203-
[contract, ownerAccount],
206+
[contract, ownerAccount, isErc721],
204207
);
205208

206209
return (

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ function MintableModule(props: ModuleInstanceProps) {
142142
throw new Error("Not an owner account");
143143
}
144144

145-
const setSaleConfigTx = MintableERC721.setSaleConfig({
145+
const setSaleConfig = isErc721
146+
? MintableERC721.setSaleConfig
147+
: MintableERC1155.setSaleConfig;
148+
const setSaleConfigTx = setSaleConfig({
146149
contract: contract,
147150
primarySaleRecipient: values.primarySaleRecipient,
148151
});
@@ -152,7 +155,7 @@ function MintableModule(props: ModuleInstanceProps) {
152155
transaction: setSaleConfigTx,
153156
});
154157
},
155-
[contract, ownerAccount],
158+
[contract, ownerAccount, isErc721],
156159
);
157160

158161
return (

0 commit comments

Comments
 (0)