@@ -24,13 +24,15 @@ import { Skeleton } from "@/components/ui/skeleton";
2424import { ToolTipLabel } from "@/components/ui/tooltip" ;
2525import { zodResolver } from "@hookform/resolvers/zod" ;
2626import { useMutation } from "@tanstack/react-query" ;
27+ import { addDays , fromUnixTime } from "date-fns" ;
2728import { useAllChainsData } from "hooks/chains/allChains" ;
2829import { useTxNotifications } from "hooks/useTxNotifications" ;
2930import { CircleAlertIcon , PlusIcon , Trash2Icon } from "lucide-react" ;
3031import { useCallback } from "react" ;
3132import { useFieldArray , useForm } from "react-hook-form" ;
3233import {
3334 type PreparedTransaction ,
35+ ZERO_ADDRESS ,
3436 getContract ,
3537 sendAndConfirmTransaction ,
3638 toTokens ,
@@ -44,7 +46,7 @@ import { CurrencySelector } from "./CurrencySelector";
4446import { ModuleCardUI , type ModuleCardUIProps } from "./module-card" ;
4547import type { ModuleInstanceProps } from "./module-instance" ;
4648
47- export type ClaimCondition = {
49+ export type ClaimConditionValue = {
4850 availableSupply : bigint ;
4951 allowlistMerkleRoot : `0x${string } `;
5052 pricePerUnit : bigint ;
@@ -55,20 +57,34 @@ export type ClaimCondition = {
5557 auxData : string ;
5658} ;
5759
60+ // TODO - for erc1155 have a UI something like this:
61+ // - show one input initially - user enters tokenId
62+ // - fetch the claim conditions for that tokenId and show the entire claim condition form with those values if they exist, or show empty form state if they don't exist
63+
64+ // TODO - don't compare with ZERO_ADDRESS we are not getting ZERO_ADDRESS as currency address and instead getting 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, (checksummed NATIVE_TOKEN_ADDRESS)
65+
66+ // TODO - fix Currency selector not showing the selected currency
67+
5868function ClaimableModule ( props : ModuleInstanceProps ) {
5969 const { contract, ownerAccount } = props ;
6070 const account = useActiveAccount ( ) ;
6171
72+ const isErc721 = props . contractInfo . name === "ClaimableERC721" ;
73+
6274 const primarySaleRecipientQuery = useReadContract (
63- ClaimableERC721 . getSaleConfig ,
75+ isErc721 ? ClaimableERC721 . getSaleConfig : ClaimableERC1155 . getSaleConfig ,
6476 {
6577 contract : contract ,
6678 } ,
6779 ) ;
80+
6881 const claimConditionQuery = useReadContract (
6982 ClaimableERC721 . getClaimCondition ,
7083 {
7184 contract : contract ,
85+ queryOptions : {
86+ enabled : isErc721 ,
87+ } ,
7288 } ,
7389 ) ;
7490
@@ -78,18 +94,17 @@ function ClaimableModule(props: ModuleInstanceProps) {
7894 client : props . contract . client ,
7995 } ) ;
8096
81- const tokenDecimals = useReadContract ( decimals , {
97+ const shouldFetchTokenDecimals =
98+ isErc721 &&
99+ claimConditionQuery . data &&
100+ claimConditionQuery . data ?. currency !== ZERO_ADDRESS ;
101+
102+ const tokenDecimalsQuery = useReadContract ( decimals , {
82103 contract : currencyContract ,
83104 queryOptions : {
84- enabled :
85- claimConditionQuery . data &&
86- claimConditionQuery . data ?. currency !==
87- "0x0000000000000000000000000000000000000000" ,
105+ enabled : shouldFetchTokenDecimals ,
88106 } ,
89107 } ) ;
90- const tokenDecimalsData = tokenDecimals . data ?? 0 ;
91-
92- const isErc721 = props . contractInfo . name === "ClaimableERC721" ;
93108
94109 const mint = useCallback (
95110 async ( values : MintFormValues ) => {
@@ -182,41 +197,64 @@ function ClaimableModule(props: ModuleInstanceProps) {
182197 return (
183198 < ClaimableModuleUI
184199 { ...props }
185- isPendingPrimarySaleRecipient = { primarySaleRecipientQuery . isPending }
186- isPendingClaimCondition = {
187- claimConditionQuery . isPending ||
188- ( claimConditionQuery . data ?. currency !==
189- "0x0000000000000000000000000000000000000000" &&
190- tokenDecimals . isPending )
191- }
192- primarySaleRecipient = { primarySaleRecipientQuery . data }
193- claimCondition = { claimConditionQuery . data }
194- setClaimCondition = { setClaimCondition }
195- setPrimarySaleRecipient = { setPrimarySaleRecipient }
196- mint = { mint }
200+ primarySaleRecipientSection = { {
201+ data : primarySaleRecipientQuery . data
202+ ? { primarySaleRecipient : primarySaleRecipientQuery . data }
203+ : undefined ,
204+ setPrimarySaleRecipient,
205+ } }
206+ claimConditionSection = { {
207+ data :
208+ // claim condition is common for all tokens
209+ isErc721 &&
210+ // claim conditions is fetched
211+ claimConditionQuery . isFetched &&
212+ // token decimals is fetched if it should be fetched
213+ ( shouldFetchTokenDecimals ? tokenDecimalsQuery . isFetched : true )
214+ ? {
215+ claimCondition : claimConditionQuery . data ,
216+ tokenDecimals : tokenDecimalsQuery . data ,
217+ }
218+ : undefined ,
219+ setClaimCondition,
220+ } }
197221 isOwnerAccount = { ! ! ownerAccount }
198222 isErc721 = { isErc721 }
199223 chainId = { props . contract . chain . id }
200- tokenDecimals = { tokenDecimalsData }
224+ mintSection = { {
225+ mint,
226+ } }
201227 />
202228 ) ;
203229}
204230
205231export function ClaimableModuleUI (
206232 props : Omit < ModuleCardUIProps , "children" | "updateButton" > & {
207- primarySaleRecipient : string | undefined ;
208- isPendingPrimarySaleRecipient : boolean ;
209- isPendingClaimCondition : boolean ;
210233 isOwnerAccount : boolean ;
211- claimCondition : ClaimCondition | undefined ;
212- setClaimCondition : ( values : ClaimConditionFormValues ) => Promise < void > ;
213- setPrimarySaleRecipient : (
214- values : PrimarySaleRecipientFormValues ,
215- ) => Promise < void > ;
216- mint : ( values : MintFormValues ) => Promise < void > ;
217234 isErc721 : boolean ;
218235 chainId : number ;
219- tokenDecimals : number ;
236+ primarySaleRecipientSection : {
237+ setPrimarySaleRecipient : (
238+ values : PrimarySaleRecipientFormValues ,
239+ ) => Promise < void > ;
240+ data :
241+ | {
242+ primarySaleRecipient : string ;
243+ }
244+ | undefined ;
245+ } ;
246+ mintSection : {
247+ mint : ( values : MintFormValues ) => Promise < void > ;
248+ } ;
249+ claimConditionSection : {
250+ setClaimCondition : ( values : ClaimConditionFormValues ) => Promise < void > ;
251+ data :
252+ | {
253+ claimCondition : ClaimConditionValue | undefined ;
254+ tokenDecimals : number | undefined ;
255+ }
256+ | undefined ;
257+ } ;
220258 } ,
221259) {
222260 return (
@@ -231,7 +269,10 @@ export function ClaimableModuleUI(
231269 Mint NFT
232270 </ AccordionTrigger >
233271 < AccordionContent className = "px-1" >
234- < MintNFTSection mint = { props . mint } isErc721 = { props . isErc721 } />
272+ < MintNFTSection
273+ mint = { props . mintSection . mint }
274+ isErc721 = { props . isErc721 }
275+ />
235276 </ AccordionContent >
236277 </ AccordionItem >
237278
@@ -240,18 +281,19 @@ export function ClaimableModuleUI(
240281 Claim Conditions
241282 </ AccordionTrigger >
242283 < AccordionContent className = "px-1" >
243- { ! props . isPendingClaimCondition ? (
284+ { props . claimConditionSection . data ? (
244285 < ClaimConditionSection
245286 isOwnerAccount = { props . isOwnerAccount }
246- primarySaleRecipient = { props . primarySaleRecipient }
247- claimCondition = { props . claimCondition }
248- update = { props . setClaimCondition }
287+ claimCondition = {
288+ props . claimConditionSection . data . claimCondition
289+ }
290+ update = { props . claimConditionSection . setClaimCondition }
249291 isErc721 = { props . isErc721 }
250292 chainId = { props . chainId }
251- tokenDecimals = { props . tokenDecimals }
293+ tokenDecimals = { props . claimConditionSection . data . tokenDecimals }
252294 />
253295 ) : (
254- < Skeleton className = "h-[74px ]" />
296+ < Skeleton className = "h-[350px ]" />
255297 ) }
256298 </ AccordionContent >
257299 </ AccordionItem >
@@ -264,11 +306,15 @@ export function ClaimableModuleUI(
264306 Primary Sale Recipient
265307 </ AccordionTrigger >
266308 < AccordionContent className = "px-1" >
267- { ! props . isPendingPrimarySaleRecipient ? (
309+ { props . primarySaleRecipientSection . data ? (
268310 < PrimarySaleRecipientSection
269311 isOwnerAccount = { props . isOwnerAccount }
270- primarySaleRecipient = { props . primarySaleRecipient }
271- update = { props . setPrimarySaleRecipient }
312+ primarySaleRecipient = {
313+ props . primarySaleRecipientSection . data . primarySaleRecipient
314+ }
315+ update = {
316+ props . primarySaleRecipientSection . setPrimarySaleRecipient
317+ }
272318 />
273319 ) : (
274320 < Skeleton className = "h-[74px]" />
@@ -282,10 +328,9 @@ export function ClaimableModuleUI(
282328}
283329
284330const claimConditionFormSchema = z . object ( {
285- tokenId : z . string ( ) . refine ( ( v ) => v . length === 0 || Number ( v ) >= 0 , {
331+ tokenId : z . string ( ) . refine ( ( v ) => BigInt ( v ) >= 0n , {
286332 message : "Invalid tokenId" ,
287333 } ) ,
288-
289334 pricePerToken : z . coerce
290335 . number ( )
291336 . min ( 0 , { message : "Invalid price per token" } )
@@ -303,62 +348,61 @@ const claimConditionFormSchema = z.object({
303348 message : "Invalid max claimable per wallet" ,
304349 } ) ,
305350
306- startTime : z . date ( ) . optional ( ) ,
307- endTime : z . date ( ) . optional ( ) ,
308-
351+ startTime : z . date ( ) ,
352+ endTime : z . date ( ) ,
309353 allowList : z . array ( z . object ( { address : addressSchema } ) ) . optional ( ) ,
310354} ) ;
311355
312356export type ClaimConditionFormValues = z . infer < typeof claimConditionFormSchema > ;
313357
314- // perform this outside else it forces too many re-renders
315- const now = Date . now ( ) / 1000 ;
316358const MAX_UINT_256 =
317359 "115792089237316195423570985008687907853269984665640564039457584007913129639935" ;
318360
361+ const defaultStartDate = addDays ( new Date ( ) , 7 ) ;
362+ const defaultEndDate = addDays ( new Date ( ) , 14 ) ;
363+
319364function ClaimConditionSection ( props : {
320- primarySaleRecipient : string | undefined ;
321- claimCondition : ClaimCondition | undefined ;
365+ claimCondition : ClaimConditionValue | undefined ;
322366 update : ( values : ClaimConditionFormValues ) => Promise < void > ;
323367 isOwnerAccount : boolean ;
324368 isErc721 : boolean ;
325369 chainId : number ;
326- tokenDecimals : number ;
370+ tokenDecimals : number | undefined ;
327371} ) {
328372 const { idToChain } = useAllChainsData ( ) ;
329373 const chain = idToChain . get ( props . chainId ) ;
374+ const { claimCondition } = props ;
330375
331376 const form = useForm < ClaimConditionFormValues > ( {
332377 resolver : zodResolver ( claimConditionFormSchema ) ,
333378 values : {
334379 tokenId : "" ,
335380 currencyAddress :
336- props . claimCondition ?. currency ===
337- "0x0000000000000000000000000000000000000000"
381+ claimCondition ?. currency === ZERO_ADDRESS
338382 ? ""
339- : props . claimCondition ?. currency ,
383+ : claimCondition ?. currency ,
340384 pricePerToken :
341- props . claimCondition ?. pricePerUnit &&
342- props . claimCondition ?. currency !==
343- "0x0000000000000000000000000000000000000000"
344- ? Number (
345- toTokens ( props . claimCondition ?. pricePerUnit , props . tokenDecimals ) ,
346- )
385+ claimCondition ?. pricePerUnit &&
386+ claimCondition ?. currency !== ZERO_ADDRESS &&
387+ props . tokenDecimals
388+ ? Number ( toTokens ( claimCondition ?. pricePerUnit , props . tokenDecimals ) )
347389 : 0 ,
348390 maxClaimableSupply :
349- props . claimCondition ?. availableSupply . toString ( ) === "0" ||
350- props . claimCondition ?. availableSupply . toString ( ) === MAX_UINT_256
391+ claimCondition ?. availableSupply . toString ( ) === "0" ||
392+ claimCondition ?. availableSupply . toString ( ) === MAX_UINT_256
351393 ? ""
352- : props . claimCondition ?. availableSupply . toString ( ) || "" ,
394+ : claimCondition ?. availableSupply . toString ( ) || "" ,
353395 maxClaimablePerWallet :
354- props . claimCondition ?. maxMintPerWallet . toString ( ) === "0" ||
355- props . claimCondition ?. maxMintPerWallet . toString ( ) === MAX_UINT_256
396+ claimCondition ?. maxMintPerWallet . toString ( ) === "0" ||
397+ claimCondition ?. maxMintPerWallet . toString ( ) === MAX_UINT_256
356398 ? ""
357- : props . claimCondition ?. maxMintPerWallet . toString ( ) || "" ,
358- startTime : new Date ( ( props . claimCondition ?. startTimestamp || now ) * 1000 ) ,
359- endTime : new Date (
360- ( props . claimCondition ?. endTimestamp || now + 604800 ) * 1000 ,
361- ) ,
399+ : claimCondition ?. maxMintPerWallet . toString ( ) || "" ,
400+ startTime : claimCondition ?. startTimestamp
401+ ? fromUnixTime ( claimCondition ?. startTimestamp )
402+ : defaultStartDate ,
403+ endTime : claimCondition ?. endTimestamp
404+ ? fromUnixTime ( claimCondition ?. endTimestamp )
405+ : defaultEndDate ,
362406 allowList : [ ] ,
363407 } ,
364408 } ) ;
@@ -466,20 +510,22 @@ function ClaimConditionSection(props: {
466510 </ div >
467511
468512 < FormFieldSetup
469- htmlFor = "startTime "
470- label = "Start & End Time "
471- isRequired = { false }
513+ htmlFor = "duration "
514+ label = "Duration "
515+ isRequired
472516 errorMessage = {
473517 form . formState . errors ?. startTime ?. message ||
474518 form . formState . errors ?. endTime ?. message
475519 }
476520 >
477- < DatePickerWithRange
478- from = { startTime || new Date ( ) }
479- to = { endTime || new Date ( ) }
480- setFrom = { ( from : Date ) => form . setValue ( "startTime" , from ) }
481- setTo = { ( to : Date ) => form . setValue ( "endTime" , to ) }
482- />
521+ < div >
522+ < DatePickerWithRange
523+ from = { startTime }
524+ to = { endTime }
525+ setFrom = { ( from : Date ) => form . setValue ( "startTime" , from ) }
526+ setTo = { ( to : Date ) => form . setValue ( "endTime" , to ) }
527+ />
528+ </ div >
483529 </ FormFieldSetup >
484530
485531 < Separator />
@@ -588,15 +634,15 @@ function PrimarySaleRecipientSection(props: {
588634 } ,
589635 } ) ;
590636
591- const updateNotificaions = useTxNotifications (
637+ const updateNotifications = useTxNotifications (
592638 "Successfully update primary sale recipient" ,
593639 "Failed to update primary sale recipient" ,
594640 ) ;
595641
596642 const updateMutation = useMutation ( {
597643 mutationFn : props . update ,
598- onSuccess : updateNotificaions . onSuccess ,
599- onError : updateNotificaions . onError ,
644+ onSuccess : updateNotifications . onSuccess ,
645+ onError : updateNotifications . onError ,
600646 } ) ;
601647
602648 const onSubmit = async ( ) => {
0 commit comments