import { FC, useContext, useEffect, useRef, useMemo } from 'react' import { useMediaQuery } from 'react-responsive' import { Text, Flex, TableCell, TableRow, HeaderRow, FormatCryptoCurrency, Anchor, Button, Box, Tooltip, } from '../primitives' import { useIntersectionObserver } from 'usehooks-ts' import LoadingSpinner from '../common/LoadingSpinner' import { useListings } from '@reservoir0x/reservoir-kit-ui' import Link from 'next/link' import { MutatorCallback } from 'swr' import { useMarketplaceChain, useTimeSince } from 'hooks' import CancelListing from 'components/buttons/CancelListing' import { Address } from 'wagmi' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faGasPump, faTag } from '@fortawesome/free-solid-svg-icons' import { NAVBAR_HEIGHT } from 'components/navbar' import { ChainContext } from 'context/ChainContextProvider' import Img from 'components/primitives/Img' import { BuyNow } from 'components/buttons' import optimizeImage from 'utils/optimizeImage' import { formatNumber } from 'utils/numbers' type Props = { address: Address | undefined isOwner: boolean } const desktopTemplateColumns = '1.25fr .75fr repeat(4, 1fr)' export const ListingsTable: FC = ({ address, isOwner }) => { const loadMoreRef = useRef(null) const loadMoreObserver = useIntersectionObserver(loadMoreRef, {}) let listingsQuery: Parameters['0'] = { maker: address, includeCriteriaMetadata: true, includeRawData: true, } const { chain } = useContext(ChainContext) if (chain.community) listingsQuery.community = chain.community const { data: listings, mutate, fetchNextPage, isFetchingPage, isValidating, } = useListings(listingsQuery) useEffect(() => { const isVisible = !!loadMoreObserver?.isIntersecting if (isVisible) { fetchNextPage() } }, [loadMoreObserver?.isIntersecting]) return ( <> {!isValidating && !isFetchingPage && listings && listings.length === 0 ? ( No listings yet ) : ( {listings.map((listing, i) => { return ( ) })} )} {isValidating && ( )} ) } type ListingTableRowProps = { listing: ReturnType['data'][0] isOwner: boolean mutate?: MutatorCallback } const ListingTableRow: FC = ({ listing, isOwner, mutate, }) => { const isSmallDevice = useMediaQuery({ maxWidth: 900 }) const { routePrefix } = useMarketplaceChain() const expiration = useTimeSince(listing?.expiration) const isOracleOrder = listing?.isNativeOffChainCancellable let criteriaData = listing?.criteria?.data const imageSrc = useMemo(() => { return optimizeImage( criteriaData?.token?.tokenId ? criteriaData?.token?.image || criteriaData?.collection?.image : criteriaData?.collection?.image, 250 ) }, [ criteriaData?.token?.tokenId, criteriaData?.token?.image, criteriaData?.collection?.image, ]) if (isSmallDevice) { return ( src} src={imageSrc} alt={`${listing?.id}`} width={36} height={36} /> {criteriaData?.collection?.name} {listing?.quantityRemaining && listing?.quantityRemaining > 1 ? ( {criteriaData?.token?.name || `#${criteriaData?.token?.tokenId}`} x{formatNumber(listing.quantityRemaining, 0, true)} ) : null} {`${listing?.source?.name}`} {expiration} {isOwner ? ( {!isOracleOrder ? ( Cancelling this order requires gas. } > ) : ( )} } /> ) : ( )} ) } return ( src} src={imageSrc as string} alt={`${criteriaData?.token?.name}`} width={48} height={48} /> {criteriaData?.collection?.name} {criteriaData?.token?.name || `#${criteriaData?.token?.tokenId}`} {formatNumber(listing.quantityRemaining)} {expiration} {`${listing?.source?.name}`} {listing?.source?.name as string} {isOwner ? ( {!isOracleOrder ? ( Cancelling this order requires gas. } > ) : ( )} } /> ) : ( )} ) } const headings = [ 'Items', 'Listed Price', 'Quantity', 'Expiration', 'Marketplace', '', ] const TableHeading = () => ( {headings.map((heading) => ( {heading} ))} )