11import { Alert , AlertTitle } from "@/components/ui/alert" ;
2+ import { Button } from "@/components/ui/button" ;
23import { Card } from "@/components/ui/card" ;
34import { useDashboardOwnedNFTs } from "@3rdweb-sdk/react/hooks/useDashboardOwnedNFTs" ;
45import { useWalletNFTs } from "@3rdweb-sdk/react/hooks/useWalletNFTs" ;
@@ -10,19 +11,18 @@ import {
1011 Select ,
1112 Spinner ,
1213 Tooltip ,
13- useModalContext ,
1414} from "@chakra-ui/react" ;
15+ import { TransactionButton } from "components/buttons/TransactionButton" ;
1516import { CurrencySelector } from "components/shared/CurrencySelector" ;
1617import { SolidityInput } from "contract-ui/components/solidity-inputs" ;
1718import { useTrack } from "hooks/analytics/useTrack" ;
1819import { useAllChainsData } from "hooks/chains/allChains" ;
19- import { useTxNotifications } from "hooks/useTxNotifications" ;
2020import { isAlchemySupported } from "lib/wallet/nfts/alchemy" ;
2121import { isMoralisSupported } from "lib/wallet/nfts/moralis" ;
2222import { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash" ;
2323import type { WalletNFT } from "lib/wallet/nfts/types" ;
2424import { CircleAlertIcon , InfoIcon } from "lucide-react" ;
25- import { useMemo } from "react" ;
25+ import { type Dispatch , type SetStateAction , useMemo , useState } from "react" ;
2626import { useForm } from "react-hook-form" ;
2727import { toast } from "sonner" ;
2828import {
@@ -58,6 +58,8 @@ import {
5858import { NFTMediaWithEmptyState } from "tw-components/nft-media" ;
5959import { shortenIfAddress } from "utils/usedapp-external" ;
6060
61+ const LIST_FORM_ID = "marketplace-list-form" ;
62+
6163type ListForm =
6264 | ( Omit < CreateListingParams , "quantity" | "currencyContractAddress" > & {
6365 currencyContractAddress : string ;
@@ -79,9 +81,9 @@ type ListForm =
7981
8082type CreateListingsFormProps = {
8183 contract : ThirdwebContract ;
82- formId : string ;
84+ actionText : string ;
85+ setOpen : Dispatch < SetStateAction < boolean > > ;
8386 type ?: "direct-listings" | "english-auctions" ;
84- setIsFormLoading : ( loading : boolean ) => void ;
8587} ;
8688
8789const auctionTimes = [
@@ -96,14 +98,15 @@ const auctionTimes = [
9698
9799export const CreateListingsForm : React . FC < CreateListingsFormProps > = ( {
98100 contract,
99- formId,
100101 type,
101- setIsFormLoading,
102+ actionText,
103+ setOpen,
102104} ) => {
103105 const trackEvent = useTrack ( ) ;
104106 const chainId = contract . chain . id ;
105107 const { idToChain } = useAllChainsData ( ) ;
106108 const network = idToChain . get ( chainId ) ;
109+ const [ isFormLoading , setIsFormLoading ] = useState ( false ) ;
107110
108111 const isSupportedChain =
109112 chainId &&
@@ -203,17 +206,10 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
203206
204207 const noNfts = ! nfts ?. length ;
205208
206- const modalContext = useModalContext ( ) ;
207-
208- const { onSuccess, onError } = useTxNotifications (
209- "NFT listed successfully" ,
210- "Failed to list NFT" ,
211- ) ;
212-
213209 return (
214210 < form
215- className = "flex flex-col gap-6"
216- id = { formId }
211+ className = "flex flex-col gap-6 pb-16 "
212+ id = { LIST_FORM_ID }
217213 onSubmit = { form . handleSubmit ( async ( formData ) => {
218214 if ( ! formData . selected || ! selectedContract ) {
219215 return ;
@@ -247,12 +243,13 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
247243 approved : true ,
248244 } ) ;
249245
250- try {
251- await sendAndConfirmTx . mutateAsync ( approveTx ) ;
252- } catch {
253- setIsFormLoading ( false ) ;
254- return toast . error ( "Failed to approve NFT for marketplace" ) ;
255- }
246+ const promise = sendAndConfirmTx . mutateAsync ( approveTx ) ;
247+ toast . promise ( promise , {
248+ loading : "Approving NFT for listing" ,
249+ success : "NFT approved succesfully" ,
250+ error : "Failed to approve NFT" ,
251+ } ) ;
252+ await promise ;
256253 }
257254
258255 if ( formData . listingType === "direct" ) {
@@ -270,12 +267,14 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
270267 pricePerToken : String ( formData . pricePerToken ) ,
271268 endTimestamp,
272269 } ) ;
273- await sendAndConfirmTx . mutateAsync ( transaction , {
274- onSuccess : ( ) => {
275- onSuccess ( ) ;
276- modalContext . onClose ( ) ;
277- } ,
278- onError,
270+
271+ const promise = sendAndConfirmTx . mutateAsync ( transaction , {
272+ onSuccess : ( ) => setOpen ( false ) ,
273+ } ) ;
274+ toast . promise ( promise , {
275+ loading : "Listing NFT" ,
276+ success : "NFT listed successfully" ,
277+ error : "Failed to list NFT" ,
279278 } ) ;
280279 } else if ( formData . listingType === "auction" ) {
281280 let minimumBidAmountWei : bigint ;
@@ -323,16 +322,15 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
323322 buyoutBidAmountWei * BigInt ( formData . quantity ) ,
324323 } ) ;
325324
326- await sendAndConfirmTx . mutateAsync ( transaction , {
325+ const promise = sendAndConfirmTx . mutateAsync ( transaction , {
327326 onSuccess : ( ) => {
328- onSuccess ( ) ;
329327 trackEvent ( {
330328 category : "marketplace" ,
331329 action : "add-listing" ,
332330 label : "success" ,
333331 network,
334332 } ) ;
335- modalContext . onClose ( ) ;
333+ setOpen ( false ) ;
336334 } ,
337335 onError : ( error ) => {
338336 trackEvent ( {
@@ -342,11 +340,16 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
342340 network,
343341 error,
344342 } ) ;
345- onError ( error ) ;
346343 } ,
347344 } ) ;
345+ toast . promise ( promise , {
346+ loading : "Creating auction" ,
347+ success : "Auction created successfully" ,
348+ error : "Failed to create auction" ,
349+ } ) ;
348350 }
349- } catch {
351+ } catch ( err ) {
352+ console . error ( err ) ;
350353 toast . error ( "Failed to list NFT" ) ;
351354 }
352355
@@ -540,6 +543,27 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
540543 < AlertTitle > No NFT selected</ AlertTitle >
541544 </ Alert >
542545 ) }
546+
547+ { /* Need to pin these at the bottom because this is a very long form */ }
548+ < div className = "fixed right-6 bottom-4 flex flex-row items-center justify-end gap-3" >
549+ < Button
550+ disabled = { isFormLoading }
551+ variant = "default"
552+ onClick = { ( ) => setOpen ( false ) }
553+ >
554+ Cancel
555+ </ Button >
556+ < TransactionButton
557+ txChainID = { contract . chain . id }
558+ isLoading = { isFormLoading }
559+ transactionCount = { 2 }
560+ form = { LIST_FORM_ID }
561+ type = "submit"
562+ colorScheme = "primary"
563+ >
564+ { actionText }
565+ </ TransactionButton >
566+ </ div >
543567 </ form >
544568 ) ;
545569} ;
0 commit comments