11"use client" ;
22import { Spinner } from "@/components/ui/Spinner/Spinner" ;
3+ import { Button } from "@/components/ui/button" ;
4+ import { DecimalInput } from "@/components/ui/decimal-input" ;
35import { Label } from "@/components/ui/label" ;
46import { SkeletonContainer } from "@/components/ui/skeleton" ;
57import { cn } from "@/lib/utils" ;
68import { useMutation , useQuery } from "@tanstack/react-query" ;
79import { 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" ;
912import { useTheme } from "next-themes" ;
13+ import Link from "next/link" ;
1014import { useState } from "react" ;
1115import { toast } from "sonner" ;
1216import {
@@ -22,10 +26,13 @@ import {
2226 type getActiveClaimCondition ,
2327 getApprovalForTransaction ,
2428} from "thirdweb/extensions/erc20" ;
25- import { useActiveAccount , useSendTransaction } from "thirdweb/react" ;
29+ import {
30+ useActiveAccount ,
31+ useActiveWallet ,
32+ useSendTransaction ,
33+ } from "thirdweb/react" ;
2634import { getClaimParams } from "thirdweb/utils" ;
2735import { tryCatch } from "utils/try-catch" ;
28- import { DecimalInput } from "../../../../../../../../../../@/components/ui/decimal-input" ;
2936import { getSDKTheme } from "../../../../../../../../components/sdk-component-theme" ;
3037import { PublicPageConnectButton } from "../../../_components/PublicPageConnectButton" ;
3138import { getCurrencyMeta } from "../../_utils/getCurrencyMeta" ;
@@ -48,12 +55,47 @@ export function ClaimTokenCardUI(props: {
4855} ) {
4956 const [ quantity , setQuantity ] = useState ( "1" ) ;
5057 const account = useActiveAccount ( ) ;
58+ const activeWallet = useActiveWallet ( ) ;
5159 const { theme } = useTheme ( ) ;
60+ const trackEvent = useTrack ( ) ;
5261 const sendClaimTx = useSendTransaction ( {
5362 payModal : {
5463 theme : getSDKTheme ( theme === "light" ? "light" : "dark" ) ,
5564 } ,
5665 } ) ;
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+
5799 const [ stepsUI , setStepsUI ] = useState <
58100 | undefined
59101 | {
@@ -69,6 +111,10 @@ export function ClaimTokenCardUI(props: {
69111 return ;
70112 }
71113
114+ trackAssetBuy ( {
115+ type : "attempt" ,
116+ } ) ;
117+
72118 setStepsUI ( undefined ) ;
73119
74120 const transaction = claimTo ( {
@@ -101,6 +147,12 @@ export function ClaimTokenCardUI(props: {
101147 approve : "error" ,
102148 claim : "idle" ,
103149 } ) ;
150+
151+ trackAssetBuy ( {
152+ type : "error" ,
153+ errorMessage : approveTxResult . error . message ,
154+ } ) ;
155+
104156 console . error ( approveTxResult . error ) ;
105157 toast . error ( "Failed to approve spending" , {
106158 description : approveTxResult . error . message ,
@@ -116,7 +168,7 @@ export function ClaimTokenCardUI(props: {
116168
117169 async function sendAndConfirm ( ) {
118170 const result = await sendClaimTx . mutateAsync ( transaction ) ;
119- await waitForReceipt ( result ) ;
171+ return await waitForReceipt ( result ) ;
120172 }
121173
122174 setStepsUI ( {
@@ -130,8 +182,14 @@ export function ClaimTokenCardUI(props: {
130182 approve : approveTx ? "success" : undefined ,
131183 claim : "error" ,
132184 } ) ;
185+
186+ trackAssetBuy ( {
187+ type : "error" ,
188+ errorMessage : claimTxResult . error . message ,
189+ } ) ;
190+
133191 console . error ( claimTxResult . error ) ;
134- toast . error ( "Failed to claim tokens" , {
192+ toast . error ( "Failed to buy tokens" , {
135193 description : claimTxResult . error . message ,
136194 } ) ;
137195 return ;
@@ -142,7 +200,13 @@ export function ClaimTokenCardUI(props: {
142200 claim : "success" ,
143201 } ) ;
144202
145- toast . success ( "Tokens claimed successfully" ) ;
203+ trackAssetBuy ( {
204+ type : "success" ,
205+ } ) ;
206+
207+ setSuccessScreen ( {
208+ txHash : claimTxResult . data . transactionHash ,
209+ } ) ;
146210 } ,
147211 } ) ;
148212
@@ -192,6 +256,51 @@ export function ClaimTokenCardUI(props: {
192256
193257 const claimParamsData = claimParamsQuery . data ;
194258
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+
195304 return (
196305 < div className = "rounded-xl border bg-card " >
197306 < div className = "border-b px-4 py-5 lg:px-5" >
@@ -225,10 +334,12 @@ export function ClaimTokenCardUI(props: {
225334 skeletonData = { `0.00 ${ props . claimConditionCurrency . symbol } ` }
226335 loadedData = {
227336 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 } `
232343 : undefined
233344 }
234345 render = { ( v ) => {
@@ -251,14 +362,16 @@ export function ClaimTokenCardUI(props: {
251362 skeletonData = { "0.00 ETH" }
252363 loadedData = {
253364 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 } `
262375 : undefined
263376 }
264377 render = { ( v ) => {
0 commit comments