1
1
"use client" ;
2
2
import { Spinner } from "@/components/ui/Spinner/Spinner" ;
3
+ import { Button } from "@/components/ui/button" ;
4
+ import { DecimalInput } from "@/components/ui/decimal-input" ;
3
5
import { Label } from "@/components/ui/label" ;
4
6
import { SkeletonContainer } from "@/components/ui/skeleton" ;
5
7
import { cn } from "@/lib/utils" ;
6
8
import { useMutation , useQuery } from "@tanstack/react-query" ;
7
9
import { TransactionButton } from "components/buttons/TransactionButton" ;
8
- import { CheckIcon , CircleIcon , XIcon } from "lucide-react" ;
10
+ import { useTrack } from "hooks/analytics/useTrack" ;
11
+ import { CheckIcon , CircleIcon , ExternalLinkIcon , XIcon } from "lucide-react" ;
9
12
import { useTheme } from "next-themes" ;
13
+ import Link from "next/link" ;
10
14
import { useState } from "react" ;
11
15
import { toast } from "sonner" ;
12
16
import {
@@ -22,10 +26,13 @@ import {
22
26
type getActiveClaimCondition ,
23
27
getApprovalForTransaction ,
24
28
} from "thirdweb/extensions/erc20" ;
25
- import { useActiveAccount , useSendTransaction } from "thirdweb/react" ;
29
+ import {
30
+ useActiveAccount ,
31
+ useActiveWallet ,
32
+ useSendTransaction ,
33
+ } from "thirdweb/react" ;
26
34
import { getClaimParams } from "thirdweb/utils" ;
27
35
import { tryCatch } from "utils/try-catch" ;
28
- import { DecimalInput } from "../../../../../../../../../../@/components/ui/decimal-input" ;
29
36
import { getSDKTheme } from "../../../../../../../../components/sdk-component-theme" ;
30
37
import { PublicPageConnectButton } from "../../../_components/PublicPageConnectButton" ;
31
38
import { getCurrencyMeta } from "../../_utils/getCurrencyMeta" ;
@@ -48,12 +55,47 @@ export function ClaimTokenCardUI(props: {
48
55
} ) {
49
56
const [ quantity , setQuantity ] = useState ( "1" ) ;
50
57
const account = useActiveAccount ( ) ;
58
+ const activeWallet = useActiveWallet ( ) ;
51
59
const { theme } = useTheme ( ) ;
60
+ const trackEvent = useTrack ( ) ;
52
61
const sendClaimTx = useSendTransaction ( {
53
62
payModal : {
54
63
theme : getSDKTheme ( theme === "light" ? "light" : "dark" ) ,
55
64
} ,
56
65
} ) ;
66
+ const [ successScreen , setSuccessScreen ] = useState <
67
+ | undefined
68
+ | {
69
+ txHash : string ;
70
+ }
71
+ > ( undefined ) ;
72
+
73
+ function trackAssetBuy (
74
+ params :
75
+ | {
76
+ type : "attempt" | "success" ;
77
+ }
78
+ | {
79
+ type : "error" ;
80
+ errorMessage : string ;
81
+ } ,
82
+ ) {
83
+ trackEvent ( {
84
+ category : "asset" ,
85
+ action : "buy" ,
86
+ label : params . type ,
87
+ contractType : "DropERC20" ,
88
+ accountAddress : account ?. address ,
89
+ walletId : activeWallet ?. id ,
90
+ chainId : props . contract . chain . id ,
91
+ ...( params . type === "error"
92
+ ? {
93
+ errorMessage : params . errorMessage ,
94
+ }
95
+ : { } ) ,
96
+ } ) ;
97
+ }
98
+
57
99
const [ stepsUI , setStepsUI ] = useState <
58
100
| undefined
59
101
| {
@@ -69,6 +111,10 @@ export function ClaimTokenCardUI(props: {
69
111
return ;
70
112
}
71
113
114
+ trackAssetBuy ( {
115
+ type : "attempt" ,
116
+ } ) ;
117
+
72
118
setStepsUI ( undefined ) ;
73
119
74
120
const transaction = claimTo ( {
@@ -101,6 +147,12 @@ export function ClaimTokenCardUI(props: {
101
147
approve : "error" ,
102
148
claim : "idle" ,
103
149
} ) ;
150
+
151
+ trackAssetBuy ( {
152
+ type : "error" ,
153
+ errorMessage : approveTxResult . error . message ,
154
+ } ) ;
155
+
104
156
console . error ( approveTxResult . error ) ;
105
157
toast . error ( "Failed to approve spending" , {
106
158
description : approveTxResult . error . message ,
@@ -116,7 +168,7 @@ export function ClaimTokenCardUI(props: {
116
168
117
169
async function sendAndConfirm ( ) {
118
170
const result = await sendClaimTx . mutateAsync ( transaction ) ;
119
- await waitForReceipt ( result ) ;
171
+ return await waitForReceipt ( result ) ;
120
172
}
121
173
122
174
setStepsUI ( {
@@ -130,8 +182,14 @@ export function ClaimTokenCardUI(props: {
130
182
approve : approveTx ? "success" : undefined ,
131
183
claim : "error" ,
132
184
} ) ;
185
+
186
+ trackAssetBuy ( {
187
+ type : "error" ,
188
+ errorMessage : claimTxResult . error . message ,
189
+ } ) ;
190
+
133
191
console . error ( claimTxResult . error ) ;
134
- toast . error ( "Failed to claim tokens" , {
192
+ toast . error ( "Failed to buy tokens" , {
135
193
description : claimTxResult . error . message ,
136
194
} ) ;
137
195
return ;
@@ -142,7 +200,13 @@ export function ClaimTokenCardUI(props: {
142
200
claim : "success" ,
143
201
} ) ;
144
202
145
- toast . success ( "Tokens claimed successfully" ) ;
203
+ trackAssetBuy ( {
204
+ type : "success" ,
205
+ } ) ;
206
+
207
+ setSuccessScreen ( {
208
+ txHash : claimTxResult . data . transactionHash ,
209
+ } ) ;
146
210
} ,
147
211
} ) ;
148
212
@@ -192,6 +256,51 @@ export function ClaimTokenCardUI(props: {
192
256
193
257
const claimParamsData = claimParamsQuery . data ;
194
258
259
+ if ( successScreen ) {
260
+ const explorerUrl =
261
+ props . chainMetadata . explorers ?. [ 0 ] ?. url ??
262
+ `https://thirdweb.com/${ props . chainMetadata . slug } ` ;
263
+
264
+ return (
265
+ < div className = "rounded-xl border bg-card p-6" >
266
+ { /* icon */ }
267
+ < div className = "flex justify-center py-8" >
268
+ < div className = "rounded-full border bg-background p-3" >
269
+ < CheckIcon className = "size-8" />
270
+ </ div >
271
+ </ div >
272
+
273
+ < div className = "mb-12" >
274
+ < h2 className = "mb-1 text-center font-bold text-xl" >
275
+ Purchase Successful
276
+ </ h2 >
277
+ < p className = "text-center text-muted-foreground text-sm" >
278
+ You have successfully purchased { quantity } { " " }
279
+ { props . symbol || "tokens" }
280
+ </ p >
281
+ </ div >
282
+
283
+ < Button className = "w-full bg-muted/50" variant = "outline" asChild >
284
+ < Link
285
+ href = { `${ explorerUrl } /tx/${ successScreen . txHash } ` }
286
+ target = "_blank"
287
+ className = "gap-1.5"
288
+ >
289
+ View Transaction{ " " }
290
+ < ExternalLinkIcon className = "size-3.5 text-muted-foreground" />
291
+ </ Link >
292
+ </ Button >
293
+
294
+ < Button
295
+ onClick = { ( ) => setSuccessScreen ( undefined ) }
296
+ className = "mt-3 w-full"
297
+ >
298
+ Buy More
299
+ </ Button >
300
+ </ div >
301
+ ) ;
302
+ }
303
+
195
304
return (
196
305
< div className = "rounded-xl border bg-card " >
197
306
< div className = "border-b px-4 py-5 lg:px-5" >
@@ -225,10 +334,12 @@ export function ClaimTokenCardUI(props: {
225
334
skeletonData = { `0.00 ${ props . claimConditionCurrency . symbol } ` }
226
335
loadedData = {
227
336
claimParamsData
228
- ? `${ toTokens (
229
- claimParamsData . pricePerTokenWei ,
230
- claimParamsData . decimals ,
231
- ) } ${ claimParamsData . symbol } `
337
+ ? claimParamsData . pricePerTokenWei === 0n
338
+ ? "FREE"
339
+ : `${ toTokens (
340
+ claimParamsData . pricePerTokenWei ,
341
+ claimParamsData . decimals ,
342
+ ) } ${ claimParamsData . symbol } `
232
343
: undefined
233
344
}
234
345
render = { ( v ) => {
@@ -251,14 +362,16 @@ export function ClaimTokenCardUI(props: {
251
362
skeletonData = { "0.00 ETH" }
252
363
loadedData = {
253
364
claimParamsData
254
- ? `${
255
- Number (
256
- toTokens (
257
- claimParamsData . pricePerTokenWei ,
258
- claimParamsData . decimals ,
259
- ) ,
260
- ) * Number ( quantity )
261
- } ${ claimParamsData . symbol } `
365
+ ? claimParamsData . pricePerTokenWei === 0n
366
+ ? "FREE"
367
+ : `${
368
+ Number (
369
+ toTokens (
370
+ claimParamsData . pricePerTokenWei ,
371
+ claimParamsData . decimals ,
372
+ ) ,
373
+ ) * Number ( quantity )
374
+ } ${ claimParamsData . symbol } `
262
375
: undefined
263
376
}
264
377
render = { ( v ) => {
0 commit comments