feat: add marketplace and trait counts to assets query (#5137)
* working total count * trait counts * marketplace counts * carousel card * undo count refactor * Filter styles * remove any cast and handle 0
This commit is contained in:
parent
ed7f126bd0
commit
1893d258b5
@ -2,12 +2,13 @@ import graphql from 'babel-plugin-relay/macro'
|
||||
import { parseEther } from 'ethers/lib/utils'
|
||||
import useInterval from 'lib/hooks/useInterval'
|
||||
import ms from 'ms.macro'
|
||||
import { GenieAsset, Rarity, SellOrder } from 'nft/types'
|
||||
import { GenieAsset } from 'nft/types'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { fetchQuery, useLazyLoadQuery, usePaginationFragment, useRelayEnvironment } from 'react-relay'
|
||||
|
||||
import { AssetPaginationQuery } from './__generated__/AssetPaginationQuery.graphql'
|
||||
import { AssetQuery, NftAssetsFilterInput, NftAssetSortableField } from './__generated__/AssetQuery.graphql'
|
||||
import { AssetQuery_nftAssets$data } from './__generated__/AssetQuery_nftAssets.graphql'
|
||||
|
||||
const assetPaginationQuery = graphql`
|
||||
fragment AssetQuery_nftAssets on Query @refetchable(queryName: "AssetPaginationQuery") {
|
||||
@ -91,6 +92,7 @@ const assetPaginationQuery = graphql`
|
||||
metadataUrl
|
||||
}
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -110,6 +112,10 @@ const assetQuery = graphql`
|
||||
}
|
||||
`
|
||||
|
||||
type NftAssetsQueryAsset = NonNullable<
|
||||
NonNullable<NonNullable<AssetQuery_nftAssets$data['nftAssets']>['edges']>[number]
|
||||
>
|
||||
|
||||
export function useAssetsQuery(
|
||||
address: string,
|
||||
orderBy: NftAssetSortableField,
|
||||
@ -152,14 +158,14 @@ export function useAssetsQuery(
|
||||
// It is especially important for this to be memoized to avoid re-rendering from polling if data is unchanged.
|
||||
const assets: GenieAsset[] = useMemo(
|
||||
() =>
|
||||
data.nftAssets?.edges?.map((queryAsset: { node: any }) => {
|
||||
data.nftAssets?.edges?.map((queryAsset: NftAssetsQueryAsset) => {
|
||||
const asset = queryAsset.node
|
||||
const ethPrice = parseEther(
|
||||
asset.listings?.edges[0]?.node.price.value?.toLocaleString('fullwide', { useGrouping: false }) ?? '0'
|
||||
).toString()
|
||||
return {
|
||||
id: asset.id,
|
||||
address: asset.collection?.nftContracts[0]?.address,
|
||||
address: asset?.collection?.nftContracts?.[0]?.address,
|
||||
notForSale: asset.listings?.edges?.length === 0,
|
||||
collectionName: asset.collection?.name,
|
||||
collectionSymbol: asset.collection?.image?.url,
|
||||
@ -176,7 +182,7 @@ export function useAssetsQuery(
|
||||
}
|
||||
: undefined,
|
||||
susFlag: asset.suspiciousFlag,
|
||||
sellorders: asset.listings?.edges.map((listingNode: { node: SellOrder }) => {
|
||||
sellorders: asset.listings?.edges.map((listingNode) => {
|
||||
return {
|
||||
...listingNode.node,
|
||||
protocolParameters: listingNode.node?.protocolParameters
|
||||
@ -186,12 +192,12 @@ export function useAssetsQuery(
|
||||
}),
|
||||
smallImageUrl: asset.smallImage?.url,
|
||||
tokenId: asset.tokenId,
|
||||
tokenType: asset.collection?.nftContracts[0]?.standard,
|
||||
// totalCount?: number, // TODO waiting for BE changes
|
||||
tokenType: asset.collection?.nftContracts?.[0]?.standard,
|
||||
totalCount: data.nftAssets?.totalCount,
|
||||
collectionIsVerified: asset.collection?.isVerified,
|
||||
rarity: {
|
||||
primaryProvider: 'Rarity Sniper', // TODO update when backend adds more providers
|
||||
providers: asset.rarities?.map((rarity: Rarity) => {
|
||||
providers: asset.rarities?.map((rarity) => {
|
||||
return {
|
||||
...rarity,
|
||||
provider: 'Rarity Sniper',
|
||||
@ -206,7 +212,7 @@ export function useAssetsQuery(
|
||||
metadataUrl: asset.metadataUrl,
|
||||
}
|
||||
}),
|
||||
[data.nftAssets?.edges]
|
||||
[data.nftAssets?.edges, data.nftAssets?.totalCount]
|
||||
)
|
||||
|
||||
return { assets, hasNext, isLoadingNext, loadNext }
|
||||
|
@ -68,6 +68,11 @@ const collectionQuery = graphql`
|
||||
value
|
||||
currency
|
||||
}
|
||||
marketplaces {
|
||||
marketplace
|
||||
listings
|
||||
floorPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,11 +94,12 @@ export function useCollectionQuery(address: string): GenieCollection | undefined
|
||||
const traits = {} as Record<string, Trait[]>
|
||||
if (queryCollection?.traits) {
|
||||
queryCollection?.traits.forEach((trait) => {
|
||||
if (trait.name && trait.values) {
|
||||
traits[trait.name] = trait.values.map((value) => {
|
||||
if (trait.name && trait.stats) {
|
||||
traits[trait.name] = trait.stats.map((stats) => {
|
||||
return {
|
||||
trait_type: trait.name,
|
||||
trait_value: value,
|
||||
trait_type: stats.name,
|
||||
trait_value: stats.value,
|
||||
trait_count: stats.assets,
|
||||
} as Trait
|
||||
})
|
||||
}
|
||||
@ -120,7 +126,15 @@ export function useCollectionQuery(address: string): GenieCollection | undefined
|
||||
}
|
||||
: {},
|
||||
traits,
|
||||
// marketplaceCount: { marketplace: string; count: number }[], // TODO add when backend supports
|
||||
marketplaceCount: queryCollection?.markets
|
||||
? market?.marketplaces?.map((market) => {
|
||||
return {
|
||||
marketplace: market.marketplace?.toLowerCase() ?? '',
|
||||
count: market.listings ?? 0,
|
||||
floorPrice: market.floorPrice ?? 0,
|
||||
}
|
||||
})
|
||||
: undefined,
|
||||
imageUrl: queryCollection?.image?.url ?? '',
|
||||
twitterUrl: queryCollection?.twitterName ?? '',
|
||||
instagram: queryCollection?.instagramName ?? undefined,
|
||||
|
@ -498,6 +498,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
<FilterButton
|
||||
isMobile={isMobile}
|
||||
isFiltersExpanded={isFiltersExpanded}
|
||||
collectionCount={collectionNfts?.[0]?.totalCount ?? 0}
|
||||
onClick={() => setFiltersExpanded(!isFiltersExpanded)}
|
||||
/>
|
||||
</TraceEvent>
|
||||
|
@ -4,15 +4,18 @@ import * as styles from 'nft/components/collection/FilterButton.css'
|
||||
import { FilterIcon } from 'nft/components/icons'
|
||||
import { buttonTextMedium } from 'nft/css/common.css'
|
||||
import { useIsCollectionLoading } from 'nft/hooks'
|
||||
import { putCommas } from 'nft/utils'
|
||||
|
||||
export const FilterButton = ({
|
||||
onClick,
|
||||
isMobile,
|
||||
isFiltersExpanded,
|
||||
collectionCount = 0,
|
||||
}: {
|
||||
isMobile: boolean
|
||||
isFiltersExpanded: boolean
|
||||
onClick: () => void
|
||||
collectionCount?: number
|
||||
}) => {
|
||||
const isCollectionNftsLoading = useIsCollectionLoading((state) => state.isCollectionNftsLoading)
|
||||
|
||||
@ -37,7 +40,11 @@ export const FilterButton = ({
|
||||
color="textPrimary"
|
||||
>
|
||||
<FilterIcon />
|
||||
{!isMobile && !isFiltersExpanded && <Box className={buttonTextMedium}> Filter</Box>}
|
||||
{!isMobile ? (
|
||||
<>
|
||||
{!isFiltersExpanded && <Box className={buttonTextMedium}> Filter • {putCommas(collectionCount)} results</Box>}
|
||||
</>
|
||||
) : null}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useIsMobile } from 'nft/hooks'
|
||||
import { fetchTrendingCollections } from 'nft/queries'
|
||||
import { TimePeriod } from 'nft/types'
|
||||
import { Suspense } from 'react'
|
||||
import { useQuery } from 'react-query'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
@ -88,11 +89,13 @@ const Banner = () => {
|
||||
{collections ? (
|
||||
<Carousel>
|
||||
{collections.map((collection) => (
|
||||
<CarouselCard
|
||||
key={collection.address}
|
||||
collection={collection}
|
||||
onClick={() => navigate(`/nfts/collection/${collection.address}`)}
|
||||
/>
|
||||
<Suspense fallback={<LoadingCarouselCard />} key={collection.address}>
|
||||
<CarouselCard
|
||||
key={collection.address}
|
||||
collection={collection}
|
||||
onClick={() => navigate(`/nfts/collection/${collection.address}`)}
|
||||
/>
|
||||
</Suspense>
|
||||
))}
|
||||
</Carousel>
|
||||
) : (
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { loadingAnimation } from 'components/Loader/styled'
|
||||
import { LoadingBubble } from 'components/Tokens/loading'
|
||||
import { useCollectionQuery } from 'graphql/data/nft/Collection'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { CollectionStatsFetcher } from 'nft/queries'
|
||||
import { Markets, TrendingCollection } from 'nft/types'
|
||||
import { formatWeiToDecimal } from 'nft/utils'
|
||||
import { useQuery } from 'react-query'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
@ -192,15 +191,7 @@ const MARKETS_ENUM_TO_NAME = {
|
||||
}
|
||||
|
||||
export const CarouselCard = ({ collection, onClick }: CarouselCardProps) => {
|
||||
const { data: collectionStats, isLoading } = useQuery(
|
||||
['trendingCollectionStats', collection.address],
|
||||
() => CollectionStatsFetcher(collection.address),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
const gqlCollection = useCollectionQuery(collection.address)
|
||||
|
||||
const theme = useTheme()
|
||||
|
||||
@ -225,22 +216,23 @@ export const CarouselCard = ({ collection, onClick }: CarouselCardProps) => {
|
||||
<HeaderOverlay />
|
||||
</CardHeaderContainer>
|
||||
<CardBottomContainer>
|
||||
{isLoading || !collectionStats ? (
|
||||
{!gqlCollection ? (
|
||||
<LoadingTable />
|
||||
) : (
|
||||
<>
|
||||
<HeaderRow>Uniswap</HeaderRow>
|
||||
<HeaderRow>{formatWeiToDecimal(collection.floor.toString())} ETH Floor</HeaderRow>
|
||||
<HeaderRow>{collectionStats.marketplaceCount?.reduce((acc, cur) => acc + cur.count, 0)} Listings</HeaderRow>
|
||||
<HeaderRow>{gqlCollection.marketplaceCount?.reduce((acc, cur) => acc + cur.count, 0)} Listings</HeaderRow>
|
||||
{MARKETS_TO_CHECK.map((market) => {
|
||||
const marketplace = collectionStats.marketplaceCount?.find(
|
||||
const marketplace = gqlCollection.marketplaceCount?.find(
|
||||
(marketplace) => marketplace.marketplace === market
|
||||
)
|
||||
return (
|
||||
<MarketplaceRow
|
||||
key={'trendingCollection' + collection.address}
|
||||
marketplace={MARKETS_ENUM_TO_NAME[market]}
|
||||
listings={marketplace?.count.toString()}
|
||||
listings={marketplace?.count?.toString()}
|
||||
floor={marketplace?.floorPrice?.toString()}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
@ -125,7 +125,7 @@ export interface GenieCollection {
|
||||
total_volume?: number
|
||||
}
|
||||
traits?: Record<string, Trait[]>
|
||||
marketplaceCount?: { marketplace: string; count: number }[]
|
||||
marketplaceCount?: { marketplace: string; count: number; floorPrice: number }[]
|
||||
imageUrl: string
|
||||
twitterUrl?: string
|
||||
instagram?: string
|
||||
|
Loading…
Reference in New Issue
Block a user