1
1
"use client" ;
2
2
3
- import { useDisclosure } from "@chakra-ui/react" ;
3
+ import {
4
+ Sheet ,
5
+ SheetContent ,
6
+ SheetHeader ,
7
+ SheetTitle ,
8
+ SheetTrigger ,
9
+ } from "@/components/ui/sheet" ;
10
+ import { FormControl , Input } from "@chakra-ui/react" ;
11
+ import { TransactionButton } from "components/buttons/TransactionButton" ;
12
+ import { useTrack } from "hooks/analytics/useTrack" ;
4
13
import { GemIcon } from "lucide-react" ;
5
- import type { ThirdwebContract } from "thirdweb" ;
6
- import { Button , Drawer } from "tw-components" ;
7
- import { NFTClaimForm } from "./claim-form" ;
14
+ import { useState } from "react" ;
15
+ import { useForm } from "react-hook-form" ;
16
+ import { toast } from "sonner" ;
17
+ import { type ThirdwebContract , ZERO_ADDRESS } from "thirdweb" ;
18
+ import { getApprovalForTransaction } from "thirdweb/extensions/erc20" ;
19
+ import { claimTo } from "thirdweb/extensions/erc721" ;
20
+ import { useActiveAccount , useSendAndConfirmTransaction } from "thirdweb/react" ;
21
+ import { Button } from "tw-components" ;
22
+ import { FormErrorMessage , FormHelperText , FormLabel } from "tw-components" ;
23
+
24
+ const CLAIM_FORM_ID = "nft-claim-form" ;
8
25
9
26
interface NFTClaimButtonProps {
10
27
contract : ThirdwebContract ;
@@ -15,26 +32,144 @@ interface NFTClaimButtonProps {
15
32
* For Edition Drop we have a dedicated ClaimTabERC1155 inside each Edition's page
16
33
*/
17
34
export const NFTClaimButton : React . FC < NFTClaimButtonProps > = ( { contract } ) => {
18
- const { isOpen, onOpen, onClose } = useDisclosure ( ) ;
35
+ const trackEvent = useTrack ( ) ;
36
+ const address = useActiveAccount ( ) ?. address ;
37
+ const { register, handleSubmit, formState } = useForm ( {
38
+ defaultValues : { amount : "1" , to : address } ,
39
+ } ) ;
40
+ const { errors } = formState ;
41
+ const sendAndConfirmTx = useSendAndConfirmTransaction ( ) ;
42
+ const account = useActiveAccount ( ) ;
43
+ const [ open , setOpen ] = useState ( false ) ;
19
44
20
45
return (
21
- < >
22
- < Drawer
23
- allowPinchZoom
24
- preserveScrollBarGap
25
- size = "lg"
26
- onClose = { onClose }
27
- isOpen = { isOpen }
28
- >
29
- < NFTClaimForm contract = { contract } />
30
- </ Drawer >
31
- < Button
32
- colorScheme = "primary"
33
- leftIcon = { < GemIcon className = "size-4" /> }
34
- onClick = { onOpen }
35
- >
36
- Claim
37
- </ Button >
38
- </ >
46
+ < Sheet open = { open } onOpenChange = { setOpen } >
47
+ < SheetTrigger asChild >
48
+ < Button colorScheme = "primary" leftIcon = { < GemIcon className = "size-4" /> } >
49
+ Claim
50
+ </ Button >
51
+ </ SheetTrigger >
52
+ < SheetContent className = "z-[10000] overflow-y-auto sm:w-[540px] sm:max-w-[90%] lg:w-[700px]" >
53
+ < SheetHeader >
54
+ < SheetTitle > Claim NFTs</ SheetTitle >
55
+ </ SheetHeader >
56
+ < form className = "mt-8 flex w-full flex-col gap-3 md:flex-row" >
57
+ < div className = "flex w-full flex-col gap-6 md:flex-row" >
58
+ < FormControl isRequired isInvalid = { ! ! errors . to } >
59
+ < FormLabel > To Address</ FormLabel >
60
+ < Input placeholder = { ZERO_ADDRESS } { ...register ( "to" ) } />
61
+ < FormHelperText > Enter the address to claim to.</ FormHelperText >
62
+ < FormErrorMessage > { errors . to ?. message } </ FormErrorMessage >
63
+ </ FormControl >
64
+ < FormControl isRequired isInvalid = { ! ! errors . amount } >
65
+ < FormLabel > Amount</ FormLabel >
66
+ < Input
67
+ type = "text"
68
+ { ...register ( "amount" , {
69
+ validate : ( value ) => {
70
+ const valueNum = Number ( value ) ;
71
+ if ( ! Number . isInteger ( valueNum ) ) {
72
+ return "Amount must be an integer" ;
73
+ }
74
+ } ,
75
+ } ) }
76
+ />
77
+ < FormHelperText > How many would you like to claim?</ FormHelperText >
78
+ < FormErrorMessage > { errors . amount ?. message } </ FormErrorMessage >
79
+ </ FormControl >
80
+ </ div >
81
+ </ form >
82
+ < div className = "mt-4 flex justify-end" >
83
+ < TransactionButton
84
+ txChainID = { contract . chain . id }
85
+ transactionCount = { 1 }
86
+ form = { CLAIM_FORM_ID }
87
+ isLoading = { formState . isSubmitting }
88
+ type = "submit"
89
+ colorScheme = "primary"
90
+ onClick = { handleSubmit ( async ( d ) => {
91
+ try {
92
+ trackEvent ( {
93
+ category : "nft" ,
94
+ action : "claim" ,
95
+ label : "attempt" ,
96
+ } ) ;
97
+ if ( ! account ) {
98
+ return toast . error ( "No account detected" ) ;
99
+ }
100
+ if ( ! d . to ) {
101
+ return toast . error (
102
+ "Please enter the address that will receive the NFT" ,
103
+ ) ;
104
+ }
105
+
106
+ const transaction = claimTo ( {
107
+ contract,
108
+ to : d . to ,
109
+ quantity : BigInt ( d . amount ) ,
110
+ from : account . address ,
111
+ } ) ;
112
+
113
+ const approveTx = await getApprovalForTransaction ( {
114
+ transaction,
115
+ account,
116
+ } ) ;
117
+
118
+ if ( approveTx ) {
119
+ const promise = sendAndConfirmTx . mutateAsync ( approveTx , {
120
+ onError : ( error ) => {
121
+ console . error ( error ) ;
122
+ } ,
123
+ } ) ;
124
+ toast . promise ( promise , {
125
+ loading : "Approving ERC20 tokens for this claim" ,
126
+ success : "Tokens approved successfully" ,
127
+ error : "Failed to approve token" ,
128
+ } ) ;
129
+
130
+ await promise ;
131
+ }
132
+
133
+ const promise = sendAndConfirmTx . mutateAsync ( transaction , {
134
+ onSuccess : ( ) => {
135
+ trackEvent ( {
136
+ category : "nft" ,
137
+ action : "claim" ,
138
+ label : "success" ,
139
+ } ) ;
140
+ setOpen ( false ) ;
141
+ } ,
142
+ onError : ( error ) => {
143
+ trackEvent ( {
144
+ category : "nft" ,
145
+ action : "claim" ,
146
+ label : "error" ,
147
+ error,
148
+ } ) ;
149
+ } ,
150
+ } ) ;
151
+
152
+ toast . promise ( promise , {
153
+ loading : "Claiming NFT(s)" ,
154
+ success : "NFT(s) claimed successfully" ,
155
+ error : "Failed to claim NFT(s)" ,
156
+ } ) ;
157
+ } catch ( error ) {
158
+ console . error ( error ) ;
159
+ toast . error ( ( error as Error ) . message || "Error claiming NFT" ) ;
160
+ trackEvent ( {
161
+ category : "nft" ,
162
+ action : "claim" ,
163
+ label : "error" ,
164
+ error,
165
+ } ) ;
166
+ }
167
+ } ) }
168
+ >
169
+ Claim NFT
170
+ </ TransactionButton >
171
+ </ div >
172
+ </ SheetContent >
173
+ </ Sheet >
39
174
) ;
40
175
} ;
0 commit comments