import { faGasPump, faTag } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useListings } from '@reservoir0x/reservoir-kit-ui' import { BuyNow } from 'components/buttons' import AddToCart from 'components/buttons/AddToCart' import CancelListing from 'components/buttons/CancelListing' import EditListing from 'components/buttons/EditListing' import LoadingSpinner from 'components/common/LoadingSpinner' import { Box, Button, Flex, FormatCryptoCurrency, TableRow, Text, Tooltip, } from 'components/primitives' import { ChainContext } from 'context/ChainContextProvider' import { useENSResolver, useMarketplaceChain, useTimeSince } from 'hooks' import Link from 'next/link' import { FC, useContext, useEffect, useRef, useState } from 'react' import { MutatorCallback } from 'swr' import { useIntersectionObserver } from 'usehooks-ts' import { formatDollar, formatNumber } from 'utils/numbers' import { OnlyUserOrdersToggle } from './OnlyUserOrdersToggle' import { zeroAddress } from 'viem' type Props = { address?: string token: Parameters['0']['token'] is1155: boolean isOwner: boolean } export const ListingsTable: FC = ({ token, address, is1155, isOwner, }) => { const loadMoreRef = useRef(null) const loadMoreObserver = useIntersectionObserver(loadMoreRef, {}) const [userOnly, setUserOnly] = useState(false) let listingsQuery: Parameters['0'] = { maker: userOnly ? address : undefined, token: token, includeCriteriaMetadata: true, includeRawData: true, sortBy: 'price', } const { chain } = useContext(ChainContext) if (chain.community) { listingsQuery.community = chain.community } const { data: listings, fetchNextPage, mutate, isValidating, isFetchingPage, isLoading, } = useListings(listingsQuery, { revalidateFirstPage: true }) const { data: userListings } = useListings({ ...listingsQuery, maker: address, }) const userHasListings = userListings.length > 0 useEffect(() => { const isVisible = !!loadMoreObserver?.isIntersecting if (isVisible) { fetchNextPage() } }, [loadMoreObserver?.isIntersecting]) return ( <> {!isValidating && !isFetchingPage && listings && listings.length === 0 ? ( No listings yet ) : ( {address && userHasListings && is1155 ? ( setUserOnly(checked)} /> ) : null} {listings.map((listing, i) => { return ( ) })} )} {isValidating && ( )} ) } type ListingTableRowProps = { listing: ReturnType['data'][0] tokenString: Parameters['0']['token'] is1155: boolean isOwner: boolean address?: string mutate?: MutatorCallback } const ListingTableRow: FC = ({ listing, tokenString, is1155, isOwner, address, mutate, }) => { const { displayName: fromDisplayName } = useENSResolver(listing.maker) const { reservoirBaseUrl } = useMarketplaceChain() const expiration = useTimeSince(listing?.expiration) const expirationText = expiration ? `Expires ${expiration}` : null const isUserListing = address?.toLowerCase() === listing.maker.toLowerCase() const isOracleOrder = listing?.isNativeOffChainCancellable const contract = tokenString?.split(':')[0] const tokenId = tokenString?.split(':')[1] const listingSourceName = listing?.source?.name const listingSourceDomain = listing?.source?.domain const listingSourceLogo = `${reservoirBaseUrl}/redirect/sources/${ listingSourceDomain || listingSourceName }/logo/v2` return ( {listing.price?.amount?.usd ? ( {formatDollar(listing.price?.amount?.usd)} ) : null} {listing?.quantityRemaining && listing?.quantityRemaining > 1 ? ( x{formatNumber(listing.quantityRemaining, 0, true)} ) : null} from {listing.maker && listing.maker !== zeroAddress ? ( {fromDisplayName} ) : ( - )} {/* Not owner, erc 721 */} {!isOwner && !is1155 ? ( ) : null} {/* Not user's listing, erc 1155 */} {!isUserListing && is1155 ? ( ) : null} {/* Owner, erc 721 or erc 1155 */} {isOwner && isUserListing ? ( <> {isOracleOrder ? ( Edit} buttonCss={{ fontSize: 14, px: '$4', py: '$2', minHeight: 36, minWidth: 80, justifyContent: 'center', }} mutate={mutate} /> ) : null} {!isOracleOrder ? ( Cancelling this order requires gas. } > ) : ( )} } /> ) : null} {expirationText} ) }