Commit 5325b5f8 authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

fix: nft waterfalls requests (#5168)

* fix: request all sweep data in parallel

* fix: trigger collection query from a wrapping screen

* load sweep for correct markets

* add preload logic for assets query

* add load query to explore table

* fix: cleanup AssetFetcherParams

* fix: preload trending collections

* fix: graphql array argument

* fix: actually use preloaded asset query

* fix: use network and suspense to actually parallelize
Co-authored-by: default avatarCharlie <charles@bachmeier.io>
parent 27936cf3
...@@ -3,8 +3,8 @@ import { parseEther } from 'ethers/lib/utils' ...@@ -3,8 +3,8 @@ import { parseEther } from 'ethers/lib/utils'
import useInterval from 'lib/hooks/useInterval' import useInterval from 'lib/hooks/useInterval'
import ms from 'ms.macro' import ms from 'ms.macro'
import { GenieAsset, Trait } from 'nft/types' import { GenieAsset, Trait } from 'nft/types'
import { useCallback, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import { fetchQuery, useLazyLoadQuery, usePaginationFragment, useRelayEnvironment } from 'react-relay' import { fetchQuery, useLazyLoadQuery, usePaginationFragment, useQueryLoader, useRelayEnvironment } from 'react-relay'
import { AssetPaginationQuery } from './__generated__/AssetPaginationQuery.graphql' import { AssetPaginationQuery } from './__generated__/AssetPaginationQuery.graphql'
import { import {
...@@ -178,22 +178,42 @@ function formatAssetQueryData(queryAsset: NftAssetsQueryAsset, totalCount?: numb ...@@ -178,22 +178,42 @@ function formatAssetQueryData(queryAsset: NftAssetsQueryAsset, totalCount?: numb
} }
} }
export function useAssetsQuery( export const ASSET_PAGE_SIZE = 25
address: string,
orderBy: NftAssetSortableField, export interface AssetFetcherParams {
asc: boolean, address: string
filter: NftAssetsFilterInput, orderBy: NftAssetSortableField
first?: number, asc: boolean
after?: string, filter: NftAssetsFilterInput
last?: number, first?: number
after?: string
last?: number
before?: string before?: string
) { }
const vars = useMemo(
() => ({ address, orderBy, asc, filter, first, after, last, before }), const defaultAssetFetcherParams: Omit<AssetQuery$variables, 'address'> = {
[address, after, asc, before, filter, first, last, orderBy] orderBy: 'PRICE',
) asc: true,
const [queryOptions, setQueryOptions] = useState({ fetchKey: 0 }) // tokenSearchQuery must be specified so that this exactly matches the initial query.
const queryData = useLazyLoadQuery<AssetQuery>(assetQuery, vars, queryOptions) filter: { listed: false, tokenSearchQuery: '' },
first: ASSET_PAGE_SIZE,
}
export function useLoadAssetsQuery(address?: string) {
const [, loadQuery] = useQueryLoader<AssetQuery>(assetQuery)
useEffect(() => {
if (address) {
loadQuery({ ...defaultAssetFetcherParams, address })
}
}, [address, loadQuery])
}
export function useLazyLoadAssetsQuery(params: AssetFetcherParams) {
const vars = useMemo(() => ({ ...defaultAssetFetcherParams, ...params }), [params])
const [fetchKey, setFetchKey] = useState(0)
// Use the store if it is available (eg from polling), or the network if it is not (eg from an incorrect preload).
const fetchPolicy = 'store-or-network'
const queryData = useLazyLoadQuery<AssetQuery>(assetQuery, vars, { fetchKey, fetchPolicy }) // this will suspend if not yet loaded
const { data, hasNext, loadNext, isLoadingNext } = usePaginationFragment<AssetPaginationQuery, any>( const { data, hasNext, loadNext, isLoadingNext } = usePaginationFragment<AssetPaginationQuery, any>(
assetPaginationQuery, assetPaginationQuery,
...@@ -208,14 +228,11 @@ export function useAssetsQuery( ...@@ -208,14 +228,11 @@ export function useAssetsQuery(
// Initiate a network request. When it resolves, refresh the UI from store (to avoid re-triggering Suspense); // Initiate a network request. When it resolves, refresh the UI from store (to avoid re-triggering Suspense);
// see: https://relay.dev/docs/guided-tour/refetching/refreshing-queries/#if-you-need-to-avoid-suspense-1. // see: https://relay.dev/docs/guided-tour/refetching/refreshing-queries/#if-you-need-to-avoid-suspense-1.
await fetchQuery<AssetQuery>(environment, assetQuery, { ...vars, first: length }).toPromise() await fetchQuery<AssetQuery>(environment, assetQuery, { ...vars, first: length }).toPromise()
setQueryOptions(({ fetchKey }) => ({ setFetchKey((fetchKey) => fetchKey + 1)
fetchKey: fetchKey + 1,
fetchPolicy: 'store-only',
}))
}, [data.nftAssets?.edges?.length, environment, vars]) }, [data.nftAssets?.edges?.length, environment, vars])
// NB: This will poll every POLLING_INTERVAL, *not* every POLLING_INTERVAL from the last successful poll. // NB: This will poll every POLLING_INTERVAL, *not* every POLLING_INTERVAL from the last successful poll.
// TODO(WEB-2004): Update useInterval to wait for the fn to complete before rescheduling. // TODO(WEB-2004): Update useInterval to wait for the fn to complete before rescheduling.
useInterval(refresh, POLLING_INTERVAL) useInterval(refresh, POLLING_INTERVAL, /* leading= */ false)
// It is especially important for this to be memoized to avoid re-rendering from polling if data is unchanged. // It is especially important for this to be memoized to avoid re-rendering from polling if data is unchanged.
const assets: GenieAsset[] = useMemo( const assets: GenieAsset[] = useMemo(
...@@ -231,19 +248,16 @@ export function useAssetsQuery( ...@@ -231,19 +248,16 @@ export function useAssetsQuery(
const DEFAULT_SWEEP_AMOUNT = 50 const DEFAULT_SWEEP_AMOUNT = 50
export function useSweepAssetsQuery({ export interface SweepFetcherParams {
contractAddress,
markets,
price,
traits,
}: {
contractAddress: string contractAddress: string
markets?: string[] markets?: string[]
price?: { high?: number | string; low?: number | string; symbol: string } price?: { high?: number | string; low?: number | string; symbol: string }
traits?: Trait[] traits?: Trait[]
}): GenieAsset[] { }
const filter: NftAssetsFilterInput = useMemo(() => {
return { function useSweepFetcherVars({ contractAddress, markets, price, traits }: SweepFetcherParams): AssetQuery$variables {
const filter: NftAssetsFilterInput = useMemo(
() => ({
listed: true, listed: true,
maxPrice: price?.high?.toString(), maxPrice: price?.high?.toString(),
minPrice: price?.low?.toString(), minPrice: price?.low?.toString(),
...@@ -255,27 +269,43 @@ export function useSweepAssetsQuery({ ...@@ -255,27 +269,43 @@ export function useSweepAssetsQuery({
: undefined, : undefined,
marketplaces: marketplaces:
markets && markets.length > 0 ? markets?.map((market) => market.toUpperCase() as NftMarketplace) : undefined, markets && markets.length > 0 ? markets?.map((market) => market.toUpperCase() as NftMarketplace) : undefined,
} }),
}, [price, traits, markets]) [markets, price?.high, price?.low, traits]
const vars: AssetQuery$variables = useMemo(() => { )
return { return useMemo(
() => ({
address: contractAddress, address: contractAddress,
orderBy: 'PRICE', orderBy: 'PRICE',
asc: true, asc: true,
first: DEFAULT_SWEEP_AMOUNT, first: DEFAULT_SWEEP_AMOUNT,
filter, filter,
}),
[contractAddress, filter]
)
}
export function useLoadSweepAssetsQuery(params: SweepFetcherParams, enabled = true) {
const [, loadQuery] = useQueryLoader<AssetQuery>(assetQuery)
const vars = useSweepFetcherVars(params)
useEffect(() => {
if (enabled) {
loadQuery(vars)
} }
}, [contractAddress, filter]) }, [loadQuery, enabled, vars])
}
const queryData = useLazyLoadQuery<AssetQuery>(assetQuery, vars) // Lazy-loads an already loaded AssetsQuery.
// This will *not* trigger a query - that must be done from a parent component to ensure proper query coalescing and to
// prevent waterfalling. Use useLoadSweepAssetsQuery to trigger the query.
export function useLazyLoadSweepAssetsQuery(params: SweepFetcherParams): GenieAsset[] {
const vars = useSweepFetcherVars(params)
const queryData = useLazyLoadQuery(assetQuery, vars, { fetchPolicy: 'store-only' }) // this will suspend if not yet loaded
const { data } = usePaginationFragment<AssetPaginationQuery, any>(assetPaginationQuery, queryData) const { data } = usePaginationFragment<AssetPaginationQuery, any>(assetPaginationQuery, queryData)
const assets: GenieAsset[] = useMemo( return useMemo<GenieAsset[]>(
() => () =>
data.nftAssets?.edges?.map((queryAsset: NftAssetsQueryAsset) => { data.nftAssets?.edges?.map((queryAsset: NftAssetsQueryAsset) => {
return formatAssetQueryData(queryAsset, data.nftAssets?.totalCount) return formatAssetQueryData(queryAsset, data.nftAssets?.totalCount)
}), }),
[data.nftAssets?.edges, data.nftAssets?.totalCount] [data.nftAssets?.edges, data.nftAssets?.totalCount]
) )
return assets
} }
import graphql from 'babel-plugin-relay/macro' import graphql from 'babel-plugin-relay/macro'
import { GenieCollection, Trait } from 'nft/types' import { GenieCollection, Trait } from 'nft/types'
import { useLazyLoadQuery } from 'react-relay' import { useEffect } from 'react'
import { useLazyLoadQuery, useQueryLoader } from 'react-relay'
import { CollectionQuery } from './__generated__/CollectionQuery.graphql' import { CollectionQuery } from './__generated__/CollectionQuery.graphql'
const collectionQuery = graphql` const collectionQuery = graphql`
query CollectionQuery($address: String!) { query CollectionQuery($addresses: [String!]!) {
nftCollections(filter: { addresses: [$address] }) { nftCollections(filter: { addresses: $addresses }) {
edges { edges {
cursor cursor
node { node {
...@@ -86,8 +87,24 @@ const collectionQuery = graphql` ...@@ -86,8 +87,24 @@ const collectionQuery = graphql`
} }
` `
export function useLoadCollectionQuery(address?: string | string[]): void {
const [, loadQuery] = useQueryLoader(collectionQuery)
useEffect(() => {
if (address) {
loadQuery({ addresses: Array.isArray(address) ? address : [address] })
}
}, [address, loadQuery])
}
// Lazy-loads an already loaded CollectionQuery.
// This will *not* trigger a query - that must be done from a parent component to ensure proper query coalescing and to
// prevent waterfalling. Use useLoadCollectionQuery to trigger the query.
export function useCollectionQuery(address: string): GenieCollection | undefined { export function useCollectionQuery(address: string): GenieCollection | undefined {
const queryData = useLazyLoadQuery<CollectionQuery>(collectionQuery, { address }) const queryData = useLazyLoadQuery<CollectionQuery>( // this will suspend if not yet loaded
collectionQuery,
{ addresses: [address] },
{ fetchPolicy: 'store-or-network' }
)
const queryCollection = queryData.nftCollections?.edges[0]?.node const queryCollection = queryData.nftCollections?.edges[0]?.node
const market = queryCollection?.markets && queryCollection?.markets[0] const market = queryCollection?.markets && queryCollection?.markets[0]
......
...@@ -5,7 +5,12 @@ import clsx from 'clsx' ...@@ -5,7 +5,12 @@ import clsx from 'clsx'
import { loadingAnimation } from 'components/Loader/styled' import { loadingAnimation } from 'components/Loader/styled'
import { parseEther } from 'ethers/lib/utils' import { parseEther } from 'ethers/lib/utils'
import { NftAssetTraitInput, NftMarketplace } from 'graphql/data/nft/__generated__/AssetQuery.graphql' import { NftAssetTraitInput, NftMarketplace } from 'graphql/data/nft/__generated__/AssetQuery.graphql'
import { useAssetsQuery } from 'graphql/data/nft/Asset' import {
ASSET_PAGE_SIZE,
AssetFetcherParams,
useLazyLoadAssetsQuery,
useLoadSweepAssetsQuery,
} from 'graphql/data/nft/Asset'
import useDebounce from 'hooks/useDebounce' import useDebounce from 'hooks/useDebounce'
import { AnimatedBox, Box } from 'nft/components/Box' import { AnimatedBox, Box } from 'nft/components/Box'
import { CollectionSearch, FilterButton } from 'nft/components/collection' import { CollectionSearch, FilterButton } from 'nft/components/collection'
...@@ -29,7 +34,7 @@ import { ...@@ -29,7 +34,7 @@ import {
} from 'nft/hooks' } from 'nft/hooks'
import { useIsCollectionLoading } from 'nft/hooks/useIsCollectionLoading' import { useIsCollectionLoading } from 'nft/hooks/useIsCollectionLoading'
import { usePriceRange } from 'nft/hooks/usePriceRange' import { usePriceRange } from 'nft/hooks/usePriceRange'
import { DropDownOption, GenieCollection, TokenType, UniformHeight, UniformHeights } from 'nft/types' import { DropDownOption, GenieCollection, Markets, TokenType, UniformHeight, UniformHeights } from 'nft/types'
import { getRarityStatus } from 'nft/utils/asset' import { getRarityStatus } from 'nft/utils/asset'
import { pluralize } from 'nft/utils/roundAndPluralize' import { pluralize } from 'nft/utils/roundAndPluralize'
import { scrollToTop } from 'nft/utils/scrollToTop' import { scrollToTop } from 'nft/utils/scrollToTop'
...@@ -42,7 +47,7 @@ import { ThemedText } from 'theme' ...@@ -42,7 +47,7 @@ import { ThemedText } from 'theme'
import { CollectionAssetLoading } from './CollectionAssetLoading' import { CollectionAssetLoading } from './CollectionAssetLoading'
import { MARKETPLACE_ITEMS } from './MarketplaceSelect' import { MARKETPLACE_ITEMS } from './MarketplaceSelect'
import { Sweep } from './Sweep' import { Sweep, useSweepFetcherParams } from './Sweep'
import { TraitChip } from './TraitChip' import { TraitChip } from './TraitChip'
interface CollectionNftsProps { interface CollectionNftsProps {
...@@ -163,11 +168,9 @@ export const LoadingButton = styled.div` ...@@ -163,11 +168,9 @@ export const LoadingButton = styled.div`
background-size: 400%; background-size: 400%;
` `
export const DEFAULT_ASSET_QUERY_AMOUNT = 25
const loadingAssets = ( const loadingAssets = (
<> <>
{Array.from(Array(DEFAULT_ASSET_QUERY_AMOUNT), (_, index) => ( {Array.from(Array(ASSET_PAGE_SIZE), (_, index) => (
<CollectionAssetLoading key={index} /> <CollectionAssetLoading key={index} />
))} ))}
</> </>
...@@ -229,17 +232,19 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie ...@@ -229,17 +232,19 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
const debouncedSearchByNameText = useDebounce(searchByNameText, 500) const debouncedSearchByNameText = useDebounce(searchByNameText, 500)
const [sweepIsOpen, setSweepOpen] = useState(false) const [sweepIsOpen, setSweepOpen] = useState(false)
// Load all sweep queries. Loading them on the parent allows lazy-loading, but avoids waterfalling requests.
const { const collectionParams = useSweepFetcherParams(contractAddress, 'others', debouncedMinPrice, debouncedMaxPrice)
assets: collectionNfts, const nftxParams = useSweepFetcherParams(contractAddress, Markets.NFTX, debouncedMinPrice, debouncedMaxPrice)
loadNext, const nft20Params = useSweepFetcherParams(contractAddress, Markets.NFT20, debouncedMinPrice, debouncedMaxPrice)
hasNext, useLoadSweepAssetsQuery(collectionParams, sweepIsOpen)
isLoadingNext, useLoadSweepAssetsQuery(nftxParams, sweepIsOpen)
} = useAssetsQuery( useLoadSweepAssetsQuery(nft20Params, sweepIsOpen)
contractAddress,
SortByQueries[sortBy].field, const assetQueryParams: AssetFetcherParams = {
SortByQueries[sortBy].asc, address: contractAddress,
{ orderBy: SortByQueries[sortBy].field,
asc: SortByQueries[sortBy].asc,
filter: {
listed: buyNow, listed: buyNow,
marketplaces: markets.length > 0 ? markets.map((market) => market.toUpperCase() as NftMarketplace) : undefined, marketplaces: markets.length > 0 ? markets.map((market) => market.toUpperCase() as NftMarketplace) : undefined,
maxPrice: debouncedMaxPrice ? parseEther(debouncedMaxPrice).toString() : undefined, maxPrice: debouncedMaxPrice ? parseEther(debouncedMaxPrice).toString() : undefined,
...@@ -252,8 +257,10 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie ...@@ -252,8 +257,10 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
}) })
: undefined, : undefined,
}, },
DEFAULT_ASSET_QUERY_AMOUNT first: ASSET_PAGE_SIZE,
) }
const { assets: collectionNfts, loadNext, hasNext, isLoadingNext } = useLazyLoadAssetsQuery(assetQueryParams)
const [uniformHeight, setUniformHeight] = useState<UniformHeight>(UniformHeights.unset) const [uniformHeight, setUniformHeight] = useState<UniformHeight>(UniformHeights.unset)
const [currentTokenPlayingMedia, setCurrentTokenPlayingMedia] = useState<string | undefined>() const [currentTokenPlayingMedia, setCurrentTokenPlayingMedia] = useState<string | undefined>()
...@@ -447,12 +454,9 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie ...@@ -447,12 +454,9 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
</SweepButton> </SweepButton>
) : null} ) : null}
</ActionsContainer> </ActionsContainer>
<Sweep {sweepIsOpen && (
contractAddress={contractAddress} <Sweep contractAddress={contractAddress} minPrice={debouncedMinPrice} maxPrice={debouncedMaxPrice} />
minPrice={debouncedMinPrice} )}
maxPrice={debouncedMaxPrice}
showSweep={sweepIsOpen && buyNow && !hasErc1155s}
/>
<Row <Row
paddingTop={!!markets.length || !!traits.length || minMaxPriceChipText ? '12' : '0'} paddingTop={!!markets.length || !!traits.length || minMaxPriceChipText ? '12' : '0'}
gap="8" gap="8"
...@@ -508,7 +512,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie ...@@ -508,7 +512,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
</Box> </Box>
</AnimatedBox> </AnimatedBox>
<InfiniteScroll <InfiniteScroll
next={() => loadNext(DEFAULT_ASSET_QUERY_AMOUNT)} next={() => loadNext(ASSET_PAGE_SIZE)}
hasMore={hasNext} hasMore={hasNext}
loader={hasNext && hasNfts ? loadingAssets : null} loader={hasNext && hasNfts ? loadingAssets : null}
dataLength={collectionNfts?.length ?? 0} dataLength={collectionNfts?.length ?? 0}
......
...@@ -2,7 +2,7 @@ import 'rc-slider/assets/index.css' ...@@ -2,7 +2,7 @@ import 'rc-slider/assets/index.css'
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import { formatEther, parseEther } from '@ethersproject/units' import { formatEther, parseEther } from '@ethersproject/units'
import { useSweepAssetsQuery } from 'graphql/data/nft/Asset' import { SweepFetcherParams, useLazyLoadSweepAssetsQuery } from 'graphql/data/nft/Asset'
import { useBag, useCollectionFilters } from 'nft/hooks' import { useBag, useCollectionFilters } from 'nft/hooks'
import { GenieAsset, Markets } from 'nft/types' import { GenieAsset, Markets } from 'nft/types'
import { calcPoolPrice, formatWeiToDecimal } from 'nft/utils' import { calcPoolPrice, formatWeiToDecimal } from 'nft/utils'
...@@ -11,8 +11,8 @@ import { useEffect, useMemo, useReducer, useState } from 'react' ...@@ -11,8 +11,8 @@ import { useEffect, useMemo, useReducer, useState } from 'react'
import styled, { useTheme } from 'styled-components/macro' import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
const SweepContainer = styled.div<{ showSweep: boolean }>` const SweepContainer = styled.div`
display: ${({ showSweep }) => (showSweep ? 'flex' : 'none')}; display: flex;
gap: 60px; gap: 60px;
margin-top: 20px; margin-top: 20px;
padding: 16px; padding: 16px;
...@@ -152,10 +152,9 @@ interface SweepProps { ...@@ -152,10 +152,9 @@ interface SweepProps {
contractAddress: string contractAddress: string
minPrice: string minPrice: string
maxPrice: string maxPrice: string
showSweep: boolean
} }
export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepProps) => { export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => {
const theme = useTheme() const theme = useTheme()
const [isItemsToggled, toggleSweep] = useReducer((state) => !state, true) const [isItemsToggled, toggleSweep] = useReducer((state) => !state, true)
...@@ -169,58 +168,22 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP ...@@ -169,58 +168,22 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP
const traits = useCollectionFilters((state) => state.traits) const traits = useCollectionFilters((state) => state.traits)
const markets = useCollectionFilters((state) => state.markets) const markets = useCollectionFilters((state) => state.markets)
const getSweepFetcherParams = (market: Markets.NFTX | Markets.NFT20 | 'others') => { const collectionParams = useSweepFetcherParams(contractAddress, 'others', minPrice, maxPrice)
const isMarketFiltered = !!markets.length const nftxParams = useSweepFetcherParams(contractAddress, Markets.NFTX, minPrice, maxPrice)
const allOtherMarkets = [Markets.Opensea, Markets.X2Y2, Markets.LooksRare] const nft20Params = useSweepFetcherParams(contractAddress, Markets.NFT20, minPrice, maxPrice)
// These calls will suspend if the query is not yet loaded.
if (isMarketFiltered) { const collectionAssets = useLazyLoadSweepAssetsQuery(collectionParams)
if (market === 'others') { const nftxAssets = useLazyLoadSweepAssetsQuery(nftxParams)
return { contractAddress, traits, markets } const nft20Assets = useLazyLoadSweepAssetsQuery(nft20Params)
}
if (!markets.includes(market)) return { contractAddress: '', traits: [], markets: [] }
}
switch (market) {
case Markets.NFTX:
case Markets.NFT20:
return {
contractAddress,
traits,
markets: [market],
price: {
low: minPrice,
high: maxPrice,
symbol: 'ETH',
},
}
case 'others':
return {
contractAddress,
traits,
markets: allOtherMarkets,
price: {
low: minPrice,
high: maxPrice,
symbol: 'ETH',
},
}
}
}
const collectionAssets = useSweepAssetsQuery(getSweepFetcherParams('others'))
const nftxCollectionAssets = useSweepAssetsQuery(getSweepFetcherParams(Markets.NFTX))
const nft20CollectionAssets = useSweepAssetsQuery(getSweepFetcherParams(Markets.NFT20))
const { sortedAssets, sortedAssetsTotalEth } = useMemo(() => { const { sortedAssets, sortedAssetsTotalEth } = useMemo(() => {
if (!collectionAssets || !nftxCollectionAssets || !nft20CollectionAssets) if (!collectionAssets || !nftxAssets || !nft20Assets)
return { sortedAssets: undefined, sortedAssetsTotalEth: BigNumber.from(0) } return { sortedAssets: undefined, sortedAssetsTotalEth: BigNumber.from(0) }
let counterNFTX = 0 let counterNFTX = 0
let counterNFT20 = 0 let counterNFT20 = 0
let jointCollections = [...nftxCollectionAssets, ...nft20CollectionAssets] let jointCollections = [...nftxAssets, ...nft20Assets]
jointCollections.forEach((asset) => { jointCollections.forEach((asset) => {
if (!asset.susFlag) { if (!asset.susFlag) {
...@@ -243,10 +206,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP ...@@ -243,10 +206,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP
(asset) => BigNumber.from(asset.priceInfo.ETHPrice).gte(0) && !asset.susFlag (asset) => BigNumber.from(asset.priceInfo.ETHPrice).gte(0) && !asset.susFlag
) )
validAssets = validAssets.slice( validAssets = validAssets.slice(0, Math.max(collectionAssets.length, nftxAssets.length, nft20Assets.length))
0,
Math.max(collectionAssets.length, nftxCollectionAssets.length, nft20CollectionAssets.length)
)
return { return {
sortedAssets: validAssets, sortedAssets: validAssets,
...@@ -255,7 +215,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP ...@@ -255,7 +215,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP
BigNumber.from(0) BigNumber.from(0)
), ),
} }
}, [collectionAssets, nftxCollectionAssets, nft20CollectionAssets]) }, [collectionAssets, nftxAssets, nft20Assets])
const { sweepItemsInBag, sweepEthPrice } = useMemo(() => { const { sweepItemsInBag, sweepEthPrice } = useMemo(() => {
const sweepItemsInBag = itemsInBag const sweepItemsInBag = itemsInBag
...@@ -366,7 +326,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP ...@@ -366,7 +326,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP
} }
return ( return (
<SweepContainer showSweep={showSweep}> <SweepContainer>
<SweepLeftmostContainer> <SweepLeftmostContainer>
<SweepHeaderContainer> <SweepHeaderContainer>
<ThemedText.SubHeaderSmall color="textPrimary" lineHeight="20px" paddingTop="6px" paddingBottom="6px"> <ThemedText.SubHeaderSmall color="textPrimary" lineHeight="20px" paddingTop="6px" paddingBottom="6px">
...@@ -434,3 +394,54 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP ...@@ -434,3 +394,54 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice, showSweep }: SweepP
</SweepContainer> </SweepContainer>
) )
} }
const ALL_OTHER_MARKETS = [Markets.Opensea, Markets.X2Y2, Markets.LooksRare]
export function useSweepFetcherParams(
contractAddress: string,
market: Markets.NFTX | Markets.NFT20 | 'others',
minPrice: string,
maxPrice: string
): SweepFetcherParams {
const traits = useCollectionFilters((state) => state.traits)
const markets = useCollectionFilters((state) => state.markets)
const isMarketFiltered = !!markets.length
return useMemo(() => {
if (isMarketFiltered) {
if (market === 'others') {
return { contractAddress, traits, markets }
}
if (!markets.includes(market)) return { contractAddress: '', traits: [], markets: [] }
}
switch (market) {
case Markets.NFTX:
case Markets.NFT20:
return {
contractAddress,
traits,
markets: [market],
price: {
low: minPrice,
high: maxPrice,
symbol: 'ETH',
},
}
case 'others':
return {
contractAddress,
traits,
markets: ALL_OTHER_MARKETS,
price: {
low: minPrice,
high: maxPrice,
symbol: 'ETH',
},
}
}
}, [contractAddress, isMarketFiltered, market, markets, maxPrice, minPrice, traits])
}
import { useLoadCollectionQuery } from 'graphql/data/nft/Collection'
import { useIsMobile } from 'nft/hooks' import { useIsMobile } from 'nft/hooks'
import { fetchTrendingCollections } from 'nft/queries' import { fetchTrendingCollections } from 'nft/queries'
import { TimePeriod } from 'nft/types' import { TimePeriod } from 'nft/types'
import { Suspense } from 'react' import { Suspense, useMemo } from 'react'
import { useQuery } from 'react-query' import { useQuery } from 'react-query'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
...@@ -80,6 +81,10 @@ const Banner = () => { ...@@ -80,6 +81,10 @@ const Banner = () => {
} }
) )
// Trigger queries for the top trending collections, so that the data is immediately available if the user clicks through.
const collectionAddresses = useMemo(() => collections?.map(({ address }) => address), [collections])
useLoadCollectionQuery(collectionAddresses)
return ( return (
<BannerContainer> <BannerContainer>
<HeaderContainer> <HeaderContainer>
......
import { Trace } from '@uniswap/analytics' import { Trace } from '@uniswap/analytics'
import { PageName } from '@uniswap/analytics-events' import { PageName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { useCollectionQuery } from 'graphql/data/nft/Collection' import { useLoadAssetsQuery } from 'graphql/data/nft/Asset'
import { useCollectionQuery, useLoadCollectionQuery } from 'graphql/data/nft/Collection'
import { MobileHoverBag } from 'nft/components/bag/MobileHoverBag' import { MobileHoverBag } from 'nft/components/bag/MobileHoverBag'
import { AnimatedBox, Box } from 'nft/components/Box' import { AnimatedBox, Box } from 'nft/components/Box'
import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection' import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection'
import { CollectionNftsAndMenuLoading } from 'nft/components/collection/CollectionNfts' import { CollectionNftsAndMenuLoading } from 'nft/components/collection/CollectionNfts'
import { CollectionPageSkeleton } from 'nft/components/collection/CollectionPageSkeleton'
import { Column, Row } from 'nft/components/Flex' import { Column, Row } from 'nft/components/Flex'
import { useBag, useCollectionFilters, useFiltersExpanded, useIsMobile } from 'nft/hooks' import { useBag, useCollectionFilters, useFiltersExpanded, useIsMobile } from 'nft/hooks'
import * as styles from 'nft/pages/collection/index.css' import * as styles from 'nft/pages/collection/index.css'
...@@ -32,7 +34,6 @@ const CollectionDisplaySection = styled(Row)` ...@@ -32,7 +34,6 @@ const CollectionDisplaySection = styled(Row)`
const Collection = () => { const Collection = () => {
const { contractAddress } = useParams() const { contractAddress } = useParams()
const isMobile = useIsMobile() const isMobile = useIsMobile()
const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded() const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded()
const { pathname } = useLocation() const { pathname } = useLocation()
...@@ -154,4 +155,21 @@ const Collection = () => { ...@@ -154,4 +155,21 @@ const Collection = () => {
) )
} }
export default Collection // The page is responsible for any queries that must be run on initial load.
// Triggering query load from the page prevents waterfalled requests, as lazy-loading them in components would prevent
// any children from rendering.
const CollectionPage = () => {
const { contractAddress } = useParams()
useLoadCollectionQuery(contractAddress)
useLoadAssetsQuery(contractAddress)
// The Collection must be wrapped in suspense so that it does not suspend the CollectionPage,
// which is needed to trigger query loads.
return (
<Suspense fallback={<CollectionPageSkeleton />}>
<Collection />
</Suspense>
)
}
export default CollectionPage
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment