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:
Charles Bachmeier 2022-11-09 10:44:42 -05:00 committed by GitHub
parent ed7f126bd0
commit 1893d258b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 35 deletions

@ -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) => (
<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