refactor: remove graphql flag and default to gql endpoints (#5151)
* remove graphql flag and old endpoints * remove unused queries * deprecate old sell order type * better null checks * merge conflict
This commit is contained in:
parent
37d2603406
commit
dbf5c63ece
@ -1,6 +1,5 @@
|
||||
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
|
||||
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
|
||||
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
|
||||
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
|
||||
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
|
||||
@ -205,12 +204,6 @@ export default function FeatureFlagModal() {
|
||||
</Header>
|
||||
<FeatureFlagGroup name="Phase 1">
|
||||
<FeatureFlagOption variant={NftVariant} value={useNftFlag()} featureFlag={FeatureFlag.nft} label="NFTs" />
|
||||
<FeatureFlagOption
|
||||
variant={NftGraphQlVariant}
|
||||
value={useNftGraphQlFlag()}
|
||||
featureFlag={FeatureFlag.nftGraphQl}
|
||||
label="NFT GraphQL Endpoints"
|
||||
/>
|
||||
</FeatureFlagGroup>
|
||||
<FeatureFlagGroup name="Debug">
|
||||
<FeatureFlagOption
|
||||
|
@ -3,5 +3,4 @@ export enum FeatureFlag {
|
||||
nft = 'nfts',
|
||||
traceJsonRpc = 'traceJsonRpc',
|
||||
multiNetworkBalances = 'multiNetworkBalances',
|
||||
nftGraphQl = 'nftGraphQl',
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
|
||||
|
||||
export function useNftGraphQlFlag(): BaseVariant {
|
||||
return useBaseFlag(FeatureFlag.nftGraphQl)
|
||||
}
|
||||
|
||||
export { BaseVariant as NftGraphQlVariant }
|
@ -4,7 +4,6 @@ import { TraceEvent } from 'analytics/TraceEvent'
|
||||
import clsx from 'clsx'
|
||||
import { loadingAnimation } from 'components/Loader/styled'
|
||||
import { parseEther } from 'ethers/lib/utils'
|
||||
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
|
||||
import { NftAssetTraitInput, NftMarketplace } from 'graphql/data/nft/__generated__/AssetQuery.graphql'
|
||||
import { useAssetsQuery } from 'graphql/data/nft/Asset'
|
||||
import useDebounce from 'hooks/useDebounce'
|
||||
@ -30,7 +29,6 @@ import {
|
||||
} from 'nft/hooks'
|
||||
import { useIsCollectionLoading } from 'nft/hooks/useIsCollectionLoading'
|
||||
import { usePriceRange } from 'nft/hooks/usePriceRange'
|
||||
import { AssetsFetcher } from 'nft/queries'
|
||||
import { DropDownOption, GenieCollection, TokenType, UniformHeight, UniformHeights } from 'nft/types'
|
||||
import { getRarityStatus } from 'nft/utils/asset'
|
||||
import { pluralize } from 'nft/utils/roundAndPluralize'
|
||||
@ -38,7 +36,6 @@ import { scrollToTop } from 'nft/utils/scrollToTop'
|
||||
import { applyFiltersFromURL, syncLocalFiltersWithURL } from 'nft/utils/urlParams'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||
import { useInfiniteQuery } from 'react-query'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
@ -223,7 +220,6 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
const reset = useCollectionFilters((state) => state.reset)
|
||||
const setMin = useCollectionFilters((state) => state.setMinPrice)
|
||||
const setMax = useCollectionFilters((state) => state.setMaxPrice)
|
||||
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
|
||||
|
||||
const toggleBag = useBag((state) => state.toggleBag)
|
||||
const bagExpanded = useBag((state) => state.bagExpanded)
|
||||
@ -235,75 +231,12 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
const [sweepIsOpen, setSweepOpen] = useState(false)
|
||||
|
||||
const {
|
||||
data: collectionAssets,
|
||||
isSuccess: AssetsFetchSuccess,
|
||||
isLoading,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
} = useInfiniteQuery(
|
||||
[
|
||||
'collectionNfts',
|
||||
{
|
||||
traits,
|
||||
contractAddress,
|
||||
markets,
|
||||
notForSale: !buyNow,
|
||||
sortBy,
|
||||
debouncedMinPrice,
|
||||
debouncedMaxPrice,
|
||||
searchText: debouncedSearchByNameText,
|
||||
},
|
||||
],
|
||||
async ({ pageParam = 0 }) => {
|
||||
let sort = undefined
|
||||
switch (sortBy) {
|
||||
case SortBy.HighToLow: {
|
||||
sort = { currentEthPrice: 'desc' }
|
||||
break
|
||||
}
|
||||
case SortBy.RareToCommon: {
|
||||
sort = { 'rarity.providers.0.rank': 1 }
|
||||
break
|
||||
}
|
||||
case SortBy.CommonToRare: {
|
||||
sort = { 'rarity.providers.0.rank': -1 }
|
||||
break
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return await AssetsFetcher({
|
||||
contractAddress,
|
||||
sort,
|
||||
markets,
|
||||
notForSale: !buyNow,
|
||||
searchText: debouncedSearchByNameText,
|
||||
pageParam,
|
||||
traits,
|
||||
price: {
|
||||
low: debouncedMinPrice,
|
||||
high: debouncedMaxPrice,
|
||||
symbol: 'ETH',
|
||||
},
|
||||
})
|
||||
},
|
||||
{
|
||||
getNextPageParam: (lastPage, pages) => {
|
||||
return lastPage?.flat().length === DEFAULT_ASSET_QUERY_AMOUNT ? pages.length : null
|
||||
},
|
||||
refetchOnReconnect: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchInterval: 5000,
|
||||
}
|
||||
)
|
||||
const {
|
||||
assets: nftQueryAssets,
|
||||
assets: collectionNfts,
|
||||
loadNext,
|
||||
hasNext,
|
||||
isLoadingNext,
|
||||
} = useAssetsQuery(
|
||||
isNftGraphQl ? contractAddress : '',
|
||||
contractAddress,
|
||||
SortByQueries[sortBy].field,
|
||||
SortByQueries[sortBy].asc,
|
||||
{
|
||||
@ -328,23 +261,9 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
const oldStateRef = useRef<CollectionFilters | null>(null)
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
const collectionNfts = useMemo(() => {
|
||||
if (
|
||||
(isNftGraphQl && !nftQueryAssets && !isLoadingNext) ||
|
||||
(!isNftGraphQl && !collectionAssets) ||
|
||||
!AssetsFetchSuccess
|
||||
)
|
||||
return undefined
|
||||
|
||||
return isNftGraphQl ? nftQueryAssets : collectionAssets?.pages.flat()
|
||||
}, [AssetsFetchSuccess, collectionAssets, isLoadingNext, isNftGraphQl, nftQueryAssets])
|
||||
|
||||
const wrappedLoadingState = isNftGraphQl ? isLoadingNext : isLoading
|
||||
const wrappedHasNext = isNftGraphQl ? hasNext : hasNextPage ?? false
|
||||
|
||||
useEffect(() => {
|
||||
setIsCollectionNftsLoading(wrappedLoadingState)
|
||||
}, [wrappedLoadingState, setIsCollectionNftsLoading])
|
||||
setIsCollectionNftsLoading(isLoadingNext)
|
||||
}, [isLoadingNext, setIsCollectionNftsLoading])
|
||||
|
||||
const hasRarity = getRarityStatus(rarityStatusCache, collectionStats?.address, collectionNfts)
|
||||
|
||||
@ -508,28 +427,24 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
<CollectionSearch />
|
||||
</ActionsSubContainer>
|
||||
{!hasErc1155s ? (
|
||||
isLoading ? (
|
||||
<LoadingButton />
|
||||
) : (
|
||||
<SweepButton
|
||||
toggled={sweepIsOpen}
|
||||
disabled={!buyNow}
|
||||
className={buttonTextMedium}
|
||||
onClick={() => {
|
||||
if (!buyNow || hasErc1155s) return
|
||||
if (!sweepIsOpen) {
|
||||
scrollToTop()
|
||||
if (!bagExpanded && !isMobile) toggleBag()
|
||||
}
|
||||
setSweepOpen(!sweepIsOpen)
|
||||
}}
|
||||
>
|
||||
<SweepIcon viewBox="0 0 24 24" width="20px" height="20px" />
|
||||
<SweepText fontWeight={600} color="currentColor" lineHeight="20px">
|
||||
Sweep
|
||||
</SweepText>
|
||||
</SweepButton>
|
||||
)
|
||||
<SweepButton
|
||||
toggled={sweepIsOpen}
|
||||
disabled={!buyNow}
|
||||
className={buttonTextMedium}
|
||||
onClick={() => {
|
||||
if (!buyNow || hasErc1155s) return
|
||||
if (!sweepIsOpen) {
|
||||
scrollToTop()
|
||||
if (!bagExpanded && !isMobile) toggleBag()
|
||||
}
|
||||
setSweepOpen(!sweepIsOpen)
|
||||
}}
|
||||
>
|
||||
<SweepIcon viewBox="0 0 24 24" width="20px" height="20px" />
|
||||
<SweepText fontWeight={600} color="currentColor" lineHeight="20px">
|
||||
Sweep
|
||||
</SweepText>
|
||||
</SweepButton>
|
||||
) : null}
|
||||
</ActionsContainer>
|
||||
<Sweep
|
||||
@ -593,12 +508,12 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
</Box>
|
||||
</AnimatedBox>
|
||||
<InfiniteScroll
|
||||
next={() => (isNftGraphQl ? loadNext(DEFAULT_ASSET_QUERY_AMOUNT) : fetchNextPage())}
|
||||
hasMore={wrappedHasNext}
|
||||
loader={wrappedHasNext && hasNfts ? loadingAssets : null}
|
||||
next={() => loadNext(DEFAULT_ASSET_QUERY_AMOUNT)}
|
||||
hasMore={hasNext}
|
||||
loader={hasNext && hasNfts ? loadingAssets : null}
|
||||
dataLength={collectionNfts?.length ?? 0}
|
||||
style={{ overflow: 'unset' }}
|
||||
className={hasNfts || wrappedLoadingState ? styles.assetList : undefined}
|
||||
className={hasNfts || isLoadingNext ? styles.assetList : undefined}
|
||||
>
|
||||
{hasNfts ? (
|
||||
Nfts
|
||||
@ -617,10 +532,8 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
|
||||
</Box>
|
||||
</EmptyCollectionWrapper>
|
||||
</Center>
|
||||
) : isNftGraphQl ? (
|
||||
<CollectionNftsLoading />
|
||||
) : (
|
||||
loadingAssets
|
||||
<CollectionNftsLoading />
|
||||
)}
|
||||
</InfiniteScroll>
|
||||
</>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import clsx from 'clsx'
|
||||
import { getDeltaArrow } from 'components/Tokens/TokenDetails/PriceChart'
|
||||
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
|
||||
import { Box, BoxProps } from 'nft/components/Box'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import { Marquee } from 'nft/components/layout/Marquee'
|
||||
@ -266,31 +265,21 @@ const statsLoadingSkeleton = (isMobile: boolean) =>
|
||||
)
|
||||
|
||||
const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMobile?: boolean } & BoxProps) => {
|
||||
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
|
||||
const uniqueOwnersPercentage =
|
||||
stats.stats && stats.stats.total_supply
|
||||
? roundWholePercentage(((stats.stats.num_owners ?? 0) / stats.stats.total_supply) * 100)
|
||||
: 0
|
||||
const uniqueOwnersPercentage = stats?.stats?.total_supply
|
||||
? roundWholePercentage(((stats.stats.num_owners ?? 0) / stats.stats.total_supply) * 100)
|
||||
: 0
|
||||
const totalSupplyStr = stats.stats ? quantityFormatter(stats.stats.total_supply ?? 0) : 0
|
||||
const listedPercentageStr =
|
||||
stats.stats && stats.stats.total_supply
|
||||
? roundWholePercentage(((stats.stats.total_listings ?? 0) / stats.stats.total_supply) * 100)
|
||||
: 0
|
||||
const listedPercentageStr = stats?.stats?.total_supply
|
||||
? roundWholePercentage(((stats.stats.total_listings ?? 0) / stats.stats.total_supply) * 100)
|
||||
: 0
|
||||
const isCollectionStatsLoading = useIsCollectionLoading((state) => state.isCollectionStatsLoading)
|
||||
|
||||
// round daily volume & floorPrice to 3 decimals or less
|
||||
const totalVolumeStr = volumeFormatter(stats.stats?.total_volume ?? 0)
|
||||
const floorPriceStr = floorFormatter(stats.stats?.floor_price ?? 0)
|
||||
// graphQL formatted %age values out of 100, whereas v3 endpoint did a decimal between 0 & 1
|
||||
// TODO: remove feature flag gated logic when graphql migration is complete
|
||||
const floorChangeStr =
|
||||
stats.stats && stats.stats.one_day_floor_change
|
||||
? Math.round(Math.abs(stats.stats.one_day_floor_change) * (isNftGraphQl ? 1 : 100))
|
||||
: 0
|
||||
const arrow =
|
||||
stats.stats && stats.stats.one_day_floor_change !== undefined
|
||||
? getDeltaArrow(stats.stats.one_day_floor_change)
|
||||
: null
|
||||
const floorChangeStr = Math.round(Math.abs(stats?.stats?.one_day_floor_change ?? 0))
|
||||
const arrow = stats?.stats?.one_day_floor_change ? getDeltaArrow(stats.stats.one_day_floor_change) : undefined
|
||||
|
||||
return (
|
||||
<Row gap={{ sm: '36', md: '60' }} {...props}>
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { sendAnalyticsEvent } from 'analytics'
|
||||
import { EventName, FilterTypes } from 'analytics/constants'
|
||||
import clsx from 'clsx'
|
||||
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
|
||||
import { Box } from 'nft/components/Box'
|
||||
import * as styles from 'nft/components/collection/Filters.css'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import { ChevronUpIcon } from 'nft/components/icons'
|
||||
import { subheadSmall } from 'nft/css/common.css'
|
||||
import { useCollectionFilters } from 'nft/hooks/useCollectionFilters'
|
||||
import { useTraitsOpen } from 'nft/hooks/useTraitsOpen'
|
||||
import { TraitPosition } from 'nft/hooks/useTraitsOpen'
|
||||
import { TraitPosition, useTraitsOpen } from 'nft/hooks/useTraitsOpen'
|
||||
import { FormEvent, useEffect, useMemo, useReducer, useState } from 'react'
|
||||
|
||||
import { Checkbox } from '../layout/Checkbox'
|
||||
@ -87,8 +85,6 @@ const MarketplaceItem = ({
|
||||
)
|
||||
}
|
||||
|
||||
const GRAPHQL_MARKETS = ['cryptopunks', 'sudoswap']
|
||||
|
||||
export const MarketplaceSelect = () => {
|
||||
const {
|
||||
addMarket,
|
||||
@ -104,22 +100,19 @@ export const MarketplaceSelect = () => {
|
||||
|
||||
const [isOpen, setOpen] = useState(!!selectedMarkets.length)
|
||||
const setTraitsOpen = useTraitsOpen((state) => state.setTraitsOpen)
|
||||
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
|
||||
|
||||
const MarketplaceItems = useMemo(
|
||||
() =>
|
||||
Object.entries(MARKETPLACE_ITEMS)
|
||||
.filter(([value]) => isNftGraphQl || !GRAPHQL_MARKETS.includes(value))
|
||||
.map(([value, title]) => (
|
||||
<MarketplaceItem
|
||||
key={value}
|
||||
title={title}
|
||||
value={value}
|
||||
count={marketCount?.[value] || 0}
|
||||
{...{ addMarket, removeMarket, isMarketSelected: selectedMarkets.includes(value) }}
|
||||
/>
|
||||
)),
|
||||
[addMarket, isNftGraphQl, marketCount, removeMarket, selectedMarkets]
|
||||
Object.entries(MARKETPLACE_ITEMS).map(([value, title]) => (
|
||||
<MarketplaceItem
|
||||
key={value}
|
||||
title={title}
|
||||
value={value}
|
||||
count={marketCount?.[value] || 0}
|
||||
{...{ addMarket, removeMarket, isMarketSelected: selectedMarkets.includes(value) }}
|
||||
/>
|
||||
)),
|
||||
[addMarket, marketCount, removeMarket, selectedMarkets]
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -7,13 +7,7 @@ import { AssetPriceDetails } from 'nft/components/details/AssetPriceDetails'
|
||||
import { Center } from 'nft/components/Flex'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { ActivityFetcher } from 'nft/queries/genie/ActivityFetcher'
|
||||
import {
|
||||
ActivityEventResponse,
|
||||
ActivityEventType,
|
||||
CollectionInfoForAsset,
|
||||
GenieAsset,
|
||||
GenieCollection,
|
||||
} from 'nft/types'
|
||||
import { ActivityEventResponse, ActivityEventType, CollectionInfoForAsset, GenieAsset } from 'nft/types'
|
||||
import { shortenAddress } from 'nft/utils/address'
|
||||
import { formatEthPrice } from 'nft/utils/currency'
|
||||
import { isAudio } from 'nft/utils/isAudio'
|
||||
@ -281,10 +275,9 @@ enum MediaType {
|
||||
interface AssetDetailsProps {
|
||||
asset: GenieAsset
|
||||
collection: CollectionInfoForAsset
|
||||
collectionStats: GenieCollection | undefined
|
||||
}
|
||||
|
||||
export const AssetDetails = ({ asset, collection, collectionStats }: AssetDetailsProps) => {
|
||||
export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
|
||||
const [dominantColor] = useState<[number, number, number]>([0, 0, 0])
|
||||
|
||||
const { rarityProvider } = useMemo(
|
||||
@ -405,12 +398,6 @@ export const AssetDetails = ({ asset, collection, collectionStats }: AssetDetail
|
||||
[isSuccess, eventsData]
|
||||
)
|
||||
|
||||
// TODO: remove after switching to graphql
|
||||
const externalUrl = collection.externalUrl ?? collectionStats?.externalUrl
|
||||
const twitterUrl = collection.twitterUrl ?? collectionStats?.twitterUrl
|
||||
const discordUrl = collection.discordUrl ?? collectionStats?.discordUrl
|
||||
const isVerified = collection.isVerified ?? collectionStats?.isVerified
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<MediaContainer>
|
||||
@ -429,7 +416,7 @@ export const AssetDetails = ({ asset, collection, collectionStats }: AssetDetail
|
||||
</MediaContainer>
|
||||
<DefaultLink to={`/nfts/collection/${asset.address}`}>
|
||||
<CollectionHeader>
|
||||
{collection.collectionName} {isVerified && <VerifiedIcon />}
|
||||
{collection.collectionName} {collection.isVerified && <VerifiedIcon />}
|
||||
</CollectionHeader>
|
||||
</DefaultLink>
|
||||
|
||||
@ -449,9 +436,7 @@ export const AssetDetails = ({ asset, collection, collectionStats }: AssetDetail
|
||||
<img src={rarityProviderLogo} alt="cardLogo" width={16} />
|
||||
</HoverImageContainer>
|
||||
<ContainerText>
|
||||
{collectionStats?.rarityVerified
|
||||
? `Verified by ${collectionStats?.name}`
|
||||
: `Ranking by ${rarity.provider === 'Genie' ? fallbackProvider : rarity.provider}`}
|
||||
{`Ranking by ${rarity.provider === 'Genie' ? fallbackProvider : rarity.provider}`}
|
||||
</ContainerText>
|
||||
</HoverContainer>
|
||||
}
|
||||
@ -514,9 +499,9 @@ export const AssetDetails = ({ asset, collection, collectionStats }: AssetDetail
|
||||
|
||||
<DescriptionText>{collection.collectionDescription}</DescriptionText>
|
||||
<SocialsContainer>
|
||||
{externalUrl && <Resource name="Website" link={`${externalUrl}`} />}
|
||||
{twitterUrl && <Resource name="Twitter" link={`https://twitter.com/${twitterUrl}`} />}
|
||||
{discordUrl && <Resource name="Discord" link={discordUrl} />}
|
||||
{collection.externalUrl && <Resource name="Website" link={`${collection.externalUrl}`} />}
|
||||
{collection.twitterUrl && <Resource name="Twitter" link={`https://twitter.com/${collection.twitterUrl}`} />}
|
||||
{collection.discordUrl && <Resource name="Discord" link={collection.discordUrl} />}
|
||||
</SocialsContainer>
|
||||
</>
|
||||
</InfoContainer>
|
||||
|
@ -2,7 +2,7 @@ import { useWeb3React } from '@web3-react/core'
|
||||
import useCopyClipboard from 'hooks/useCopyClipboard'
|
||||
import { CancelListingIcon, MinusIcon, PlusIcon } from 'nft/components/icons'
|
||||
import { useBag } from 'nft/hooks'
|
||||
import { CollectionInfoForAsset, Deprecated_SellOrder, GenieAsset, SellOrder, TokenType } from 'nft/types'
|
||||
import { CollectionInfoForAsset, GenieAsset, TokenType } from 'nft/types'
|
||||
import { ethNumberStandardFormatter, formatEthPrice, getMarketplaceIcon, timeLeft, useUsdPrice } from 'nft/utils'
|
||||
import { shortenAddress } from 'nft/utils/address'
|
||||
import { useMemo } from 'react'
|
||||
@ -203,9 +203,7 @@ const OwnerInformationContainer = styled.div`
|
||||
export const OwnerContainer = ({ asset }: { asset: GenieAsset }) => {
|
||||
const listing = asset.sellorders && asset.sellorders.length > 0 ? asset.sellorders[0] : undefined
|
||||
const cheapestOrder = asset.sellorders && asset.sellorders.length > 0 ? asset.sellorders[0] : undefined
|
||||
const expirationDate = cheapestOrder
|
||||
? new Date((cheapestOrder as Deprecated_SellOrder).orderClosingDate ?? (cheapestOrder as SellOrder).endAt)
|
||||
: undefined
|
||||
const expirationDate = cheapestOrder ? new Date(cheapestOrder.endAt) : undefined
|
||||
const USDPrice = useUsdPrice(asset)
|
||||
|
||||
const navigate = useNavigate()
|
||||
@ -288,9 +286,7 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
|
||||
const { account } = useWeb3React()
|
||||
|
||||
const cheapestOrder = asset.sellorders && asset.sellorders.length > 0 ? asset.sellorders[0] : undefined
|
||||
const expirationDate = cheapestOrder
|
||||
? new Date((cheapestOrder as Deprecated_SellOrder).orderClosingDate ?? (cheapestOrder as SellOrder).endAt)
|
||||
: undefined
|
||||
const expirationDate = cheapestOrder ? new Date(cheapestOrder.endAt) : undefined
|
||||
|
||||
const itemsInBag = useBag((s) => s.itemsInBag)
|
||||
const addAssetsToBag = useBag((s) => s.addAssetsToBag)
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
|
||||
import { useNftBalanceQuery } from 'graphql/data/nft/NftBalance'
|
||||
import { AnimatedBox, Box } from 'nft/components/Box'
|
||||
import { assetList } from 'nft/components/collection/CollectionNfts.css'
|
||||
@ -18,11 +17,11 @@ import {
|
||||
useWalletCollections,
|
||||
} from 'nft/hooks'
|
||||
import { ScreenBreakpointsPaddings } from 'nft/pages/collection/index.css'
|
||||
import { fetchWalletAssets, OSCollectionsFetcher } from 'nft/queries'
|
||||
import { OSCollectionsFetcher } from 'nft/queries'
|
||||
import { ProfilePageStateType, WalletAsset, WalletCollection } from 'nft/types'
|
||||
import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'
|
||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||
import { useInfiniteQuery, useQuery } from 'react-query'
|
||||
import { useQuery } from 'react-query'
|
||||
import { useSpring } from 'react-spring'
|
||||
import styled from 'styled-components/macro'
|
||||
import shallow from 'zustand/shallow'
|
||||
@ -30,7 +29,6 @@ import shallow from 'zustand/shallow'
|
||||
import { EmptyWalletContent } from './EmptyWalletContent'
|
||||
import { ProfileAccountDetails } from './ProfileAccountDetails'
|
||||
import * as styles from './ProfilePage.css'
|
||||
import { ProfileBodyLoadingSkeleton } from './ProfilePageLoadingSkeleton'
|
||||
import { WalletAssetDisplay } from './WalletAssetDisplay'
|
||||
|
||||
const SellModeButton = styled.button<{ active: boolean }>`
|
||||
@ -65,12 +63,8 @@ export const ProfilePage = () => {
|
||||
const collectionFilters = useWalletCollections((state) => state.collectionFilters)
|
||||
const setCollectionFilters = useWalletCollections((state) => state.setCollectionFilters)
|
||||
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
|
||||
const walletAssets = useWalletCollections((state) => state.walletAssets)
|
||||
const setWalletAssets = useWalletCollections((state) => state.setWalletAssets)
|
||||
const setDisplayAssets = useWalletCollections((state) => state.setDisplayAssets)
|
||||
const walletCollections = useWalletCollections((state) => state.walletCollections)
|
||||
const setWalletCollections = useWalletCollections((state) => state.setWalletCollections)
|
||||
const listFilter = useWalletCollections((state) => state.listFilter)
|
||||
const { isSellMode, resetSellAssets, setIsSellMode } = useSellAsset(
|
||||
({ isSellMode, reset, setIsSellMode }) => ({
|
||||
isSellMode,
|
||||
@ -85,7 +79,6 @@ export const ProfilePage = () => {
|
||||
const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
|
||||
const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded()
|
||||
const isMobile = useIsMobile()
|
||||
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
|
||||
|
||||
const handleSellModeClick = useCallback(() => {
|
||||
resetSellAssets()
|
||||
@ -93,7 +86,7 @@ export const ProfilePage = () => {
|
||||
setBagExpanded({ bagExpanded: !isSellMode })
|
||||
}, [isSellMode, resetSellAssets, setBagExpanded, setIsSellMode])
|
||||
|
||||
const { data: ownerCollections, isLoading: collectionsAreLoading } = useQuery(
|
||||
const { data: ownerCollections } = useQuery(
|
||||
['ownerCollections', address],
|
||||
() => OSCollectionsFetcher({ params: { asset_owner: address, offset: '0', limit: '300' } }),
|
||||
{
|
||||
@ -102,49 +95,10 @@ export const ProfilePage = () => {
|
||||
)
|
||||
|
||||
const {
|
||||
data: ownerAssetsData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isSuccess,
|
||||
isLoading: assetsAreLoading,
|
||||
} = useInfiniteQuery(
|
||||
['ownerAssets', address, collectionFilters],
|
||||
async ({ pageParam = 0 }) => {
|
||||
return await fetchWalletAssets({
|
||||
ownerAddress: address ?? '',
|
||||
collectionAddresses: collectionFilters,
|
||||
pageParam,
|
||||
})
|
||||
},
|
||||
{
|
||||
getNextPageParam: (lastPage, pages) => {
|
||||
return lastPage?.flat().length === DEFAULT_WALLET_ASSET_QUERY_AMOUNT ? pages.length : null
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
}
|
||||
)
|
||||
|
||||
const anyQueryIsLoading = collectionsAreLoading || assetsAreLoading
|
||||
|
||||
const {
|
||||
walletAssets: gqlWalletAssets,
|
||||
walletAssets: ownerAssets,
|
||||
loadNext,
|
||||
hasNext,
|
||||
} = useNftBalanceQuery(isNftGraphQl ? address : '', collectionFilters, DEFAULT_WALLET_ASSET_QUERY_AMOUNT)
|
||||
|
||||
const ownerAssets = useMemo(
|
||||
() => (isNftGraphQl ? gqlWalletAssets : isSuccess ? ownerAssetsData?.pages.flat() : []),
|
||||
[isNftGraphQl, gqlWalletAssets, isSuccess, ownerAssetsData]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
!isNftGraphQl && setWalletAssets(ownerAssets?.flat() ?? [])
|
||||
}, [ownerAssets, setWalletAssets, isNftGraphQl])
|
||||
|
||||
useEffect(() => {
|
||||
!isNftGraphQl && setDisplayAssets(walletAssets, listFilter)
|
||||
}, [walletAssets, listFilter, setDisplayAssets, isNftGraphQl])
|
||||
} = useNftBalanceQuery(address, collectionFilters, DEFAULT_WALLET_ASSET_QUERY_AMOUNT)
|
||||
|
||||
useEffect(() => {
|
||||
ownerCollections && setWalletCollections(ownerCollections)
|
||||
@ -156,9 +110,7 @@ export const ProfilePage = () => {
|
||||
|
||||
return (
|
||||
<ProfilePageColumn width="full" paddingTop={{ sm: `${PADDING}`, md: '40' }}>
|
||||
{anyQueryIsLoading && !isNftGraphQl ? (
|
||||
<ProfileBodyLoadingSkeleton />
|
||||
) : ownerAssets?.length === 0 ? (
|
||||
{ownerAssets?.length === 0 ? (
|
||||
<EmptyWalletContent />
|
||||
) : (
|
||||
<Row alignItems="flex-start" position="relative">
|
||||
@ -200,8 +152,8 @@ export const ProfilePage = () => {
|
||||
/>
|
||||
</Row>
|
||||
<InfiniteScroll
|
||||
next={() => (isNftGraphQl ? loadNext(DEFAULT_WALLET_ASSET_QUERY_AMOUNT) : fetchNextPage())}
|
||||
hasMore={isNftGraphQl ? hasNext : hasNextPage ?? false}
|
||||
next={() => loadNext(DEFAULT_WALLET_ASSET_QUERY_AMOUNT)}
|
||||
hasMore={hasNext}
|
||||
loader={
|
||||
<Center>
|
||||
<LoadingSparkle />
|
||||
@ -270,19 +222,12 @@ export const ProfilePage = () => {
|
||||
|
||||
const SelectAllButton = ({ ownerAssets }: { ownerAssets: WalletAsset[] }) => {
|
||||
const [isAllSelected, setIsAllSelected] = useState(false)
|
||||
const displayAssets = useWalletCollections((state) => state.displayAssets)
|
||||
const selectSellAsset = useSellAsset((state) => state.selectSellAsset)
|
||||
const resetSellAssets = useSellAsset((state) => state.reset)
|
||||
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
|
||||
|
||||
const allAssets = useMemo(
|
||||
() => (isNftGraphQl ? ownerAssets : displayAssets),
|
||||
[isNftGraphQl, ownerAssets, displayAssets]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (isAllSelected) {
|
||||
allAssets.forEach((asset) => selectSellAsset(asset))
|
||||
ownerAssets.forEach((asset) => selectSellAsset(asset))
|
||||
} else {
|
||||
resetSellAssets()
|
||||
}
|
||||
|
@ -283,16 +283,6 @@ const borderWidth = ['0px', '0.5px', '1px', '1.5px', '2px', '3px', '4px']
|
||||
|
||||
const borderStyle = ['none', 'solid'] as const
|
||||
|
||||
// TODO: remove when code is done being ported over
|
||||
// I'm leaving this here as a reference of the old breakpoints while we port over the new code
|
||||
// tabletSm: 656,
|
||||
// tablet: 708,
|
||||
// tabletL: 784,
|
||||
// tabletXl: 830,
|
||||
// desktop: 948,
|
||||
// desktopL: 1030,
|
||||
// desktopXl: 1260,
|
||||
|
||||
export const breakpoints = {
|
||||
sm: 640,
|
||||
md: 768,
|
||||
|
@ -1,13 +1,9 @@
|
||||
import { PageName } from 'analytics/constants'
|
||||
import { Trace } from 'analytics/Trace'
|
||||
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
|
||||
import { useDetailsQuery } from 'graphql/data/nft/Details'
|
||||
import { AssetDetails } from 'nft/components/details/AssetDetails'
|
||||
import { AssetPriceDetails } from 'nft/components/details/AssetPriceDetails'
|
||||
import { fetchSingleAsset } from 'nft/queries'
|
||||
import { CollectionStatsFetcher } from 'nft/queries'
|
||||
import { useMemo } from 'react'
|
||||
import { useQuery } from 'react-query'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
@ -41,28 +37,9 @@ const AssetPriceDetailsContainer = styled.div`
|
||||
|
||||
const Asset = () => {
|
||||
const { tokenId = '', contractAddress = '' } = useParams()
|
||||
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
|
||||
const data = useDetailsQuery(contractAddress, tokenId)
|
||||
|
||||
const { data } = useQuery(
|
||||
['assetDetail', contractAddress, tokenId],
|
||||
() => fetchSingleAsset({ contractAddress, tokenId }),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
}
|
||||
)
|
||||
const gqlData = useDetailsQuery(contractAddress, tokenId)
|
||||
|
||||
const asset = useMemo(() => (isNftGraphQl ? gqlData && gqlData[0] : data && data[0]), [data, gqlData, isNftGraphQl])
|
||||
const collection = useMemo(
|
||||
() => (isNftGraphQl ? gqlData && gqlData[1] : data && data[1]),
|
||||
[data, gqlData, isNftGraphQl]
|
||||
)
|
||||
|
||||
const { data: collectionStats } = useQuery(['collectionStats', contractAddress], () =>
|
||||
CollectionStatsFetcher(contractAddress)
|
||||
)
|
||||
const [asset, collection] = useMemo(() => data ?? [], [data])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -73,7 +50,7 @@ const Asset = () => {
|
||||
>
|
||||
{asset && collection ? (
|
||||
<AssetContainer>
|
||||
<AssetDetails collection={collection} asset={asset} collectionStats={collectionStats} />
|
||||
<AssetDetails collection={collection} asset={asset} />
|
||||
<AssetPriceDetailsContainer>
|
||||
<AssetPriceDetails collection={collection} asset={asset} />
|
||||
</AssetPriceDetailsContainer>
|
||||
|
@ -1,19 +1,16 @@
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { PageName } from 'analytics/constants'
|
||||
import { Trace } from 'analytics/Trace'
|
||||
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
|
||||
import { useCollectionQuery } from 'graphql/data/nft/Collection'
|
||||
import { MobileHoverBag } from 'nft/components/bag/MobileHoverBag'
|
||||
import { AnimatedBox, Box } from 'nft/components/Box'
|
||||
import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection'
|
||||
import { CollectionNftsAndMenuLoading } from 'nft/components/collection/CollectionNfts'
|
||||
import { Column, Row } from 'nft/components/Flex'
|
||||
import { useBag, useCollectionFilters, useFiltersExpanded, useIsCollectionLoading, useIsMobile } from 'nft/hooks'
|
||||
import { useBag, useCollectionFilters, useFiltersExpanded, useIsMobile } from 'nft/hooks'
|
||||
import * as styles from 'nft/pages/collection/index.css'
|
||||
import { CollectionStatsFetcher } from 'nft/queries'
|
||||
import { GenieCollection } from 'nft/types'
|
||||
import { Suspense, useEffect, useMemo } from 'react'
|
||||
import { useQuery } from 'react-query'
|
||||
import { Suspense, useEffect } from 'react'
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
import { useSpring } from 'react-spring'
|
||||
import styled from 'styled-components/macro'
|
||||
@ -35,7 +32,6 @@ const CollectionDisplaySection = styled(Row)`
|
||||
|
||||
const Collection = () => {
|
||||
const { contractAddress } = useParams()
|
||||
const setIsCollectionStatsLoading = useIsCollectionLoading((state) => state.setIsCollectionStatsLoading)
|
||||
|
||||
const isMobile = useIsMobile()
|
||||
const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded()
|
||||
@ -44,23 +40,9 @@ const Collection = () => {
|
||||
const isActivityToggled = pathname.includes('/activity')
|
||||
const setMarketCount = useCollectionFilters((state) => state.setMarketCount)
|
||||
const isBagExpanded = useBag((state) => state.bagExpanded)
|
||||
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
const { data: queryCollection, isLoading } = useQuery(['collectionStats', contractAddress], () =>
|
||||
CollectionStatsFetcher(contractAddress as string)
|
||||
)
|
||||
|
||||
const gqlCollection = useCollectionQuery(contractAddress as string)
|
||||
|
||||
const collectionStats = useMemo(
|
||||
() => (isNftGraphQl ? gqlCollection : queryCollection),
|
||||
[isNftGraphQl, gqlCollection, queryCollection]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setIsCollectionStatsLoading(isLoading)
|
||||
}, [isLoading, setIsCollectionStatsLoading])
|
||||
const collectionStats = useCollectionQuery(contractAddress as string)
|
||||
|
||||
const { gridX, gridWidthOffset } = useSpring({
|
||||
gridX: isFiltersExpanded ? FILTER_WIDTH : 0,
|
||||
@ -100,26 +82,22 @@ const Collection = () => {
|
||||
{' '}
|
||||
<Box width="full" height="276">
|
||||
<Box width="full" height="276">
|
||||
{isLoading ? (
|
||||
<CollectionBannerLoading />
|
||||
) : (
|
||||
<Box
|
||||
as={collectionStats?.bannerImageUrl ? 'img' : 'div'}
|
||||
height="full"
|
||||
width="full"
|
||||
src={
|
||||
collectionStats?.bannerImageUrl
|
||||
? `${collectionStats.bannerImageUrl}?w=${window.innerWidth}`
|
||||
: undefined
|
||||
}
|
||||
className={isLoading ? styles.loadingBanner : styles.bannerImage}
|
||||
background="none"
|
||||
/>
|
||||
)}
|
||||
<Box
|
||||
as={collectionStats?.bannerImageUrl ? 'img' : 'div'}
|
||||
height="full"
|
||||
width="full"
|
||||
src={
|
||||
collectionStats?.bannerImageUrl
|
||||
? `${collectionStats.bannerImageUrl}?w=${window.innerWidth}`
|
||||
: undefined
|
||||
}
|
||||
className={styles.bannerImage}
|
||||
background="none"
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<CollectionDescriptionSection>
|
||||
{(isLoading || collectionStats !== undefined) && (
|
||||
{collectionStats && (
|
||||
<CollectionStats stats={collectionStats || ({} as GenieCollection)} isMobile={isMobile} />
|
||||
)}
|
||||
<div id="nft-anchor" />
|
||||
@ -153,7 +131,7 @@ const Collection = () => {
|
||||
/>
|
||||
)
|
||||
: contractAddress &&
|
||||
(isLoading || collectionStats !== undefined) && (
|
||||
collectionStats && (
|
||||
<Suspense fallback={<CollectionNftsAndMenuLoading />}>
|
||||
<CollectionNfts
|
||||
collectionStats={collectionStats || ({} as GenieCollection)}
|
||||
@ -167,7 +145,7 @@ const Collection = () => {
|
||||
</>
|
||||
) : (
|
||||
// TODO: Put no collection asset page here
|
||||
!isLoading && <div className={styles.noCollectionAssets}>No collection assets exist at this address</div>
|
||||
<div className={styles.noCollectionAssets}>No collection assets exist at this address</div>
|
||||
)}
|
||||
</Column>
|
||||
</Trace>
|
||||
|
@ -1,126 +0,0 @@
|
||||
import { parseEther } from '@ethersproject/units'
|
||||
|
||||
import { Trait } from '../../hooks/useCollectionFilters'
|
||||
import { AssetPayload, CollectionSort, GenieAsset } from '../../types'
|
||||
|
||||
export const formatTraits = (traits: Trait[]) => {
|
||||
const traitObj: Record<string, string[]> = {}
|
||||
const nonMetaTraits = traits.filter((el) => el.trait_type !== 'Number of traits')
|
||||
for (const trait of nonMetaTraits) {
|
||||
if (!traitObj[trait.trait_type]) traitObj[trait.trait_type] = [trait.trait_value]
|
||||
else traitObj[trait.trait_type].push(trait.trait_value)
|
||||
}
|
||||
|
||||
return traitObj
|
||||
}
|
||||
|
||||
const formatPrice = (x: number | string) => parseEther(x.toString()).toString()
|
||||
|
||||
export const AssetsFetcher = async ({
|
||||
contractAddress,
|
||||
tokenId,
|
||||
sort,
|
||||
markets,
|
||||
price,
|
||||
rarityRange,
|
||||
traits,
|
||||
searchText,
|
||||
notForSale,
|
||||
pageParam,
|
||||
}: {
|
||||
contractAddress: string
|
||||
tokenId?: string
|
||||
offset?: number
|
||||
sort?: CollectionSort
|
||||
markets?: string[]
|
||||
price?: { high?: number | string; low?: number | string; symbol: string }
|
||||
rarityRange?: Record<string, unknown>
|
||||
traits?: Trait[]
|
||||
searchText?: string
|
||||
notForSale?: boolean
|
||||
pageParam: number
|
||||
}): Promise<GenieAsset[] | undefined> => {
|
||||
const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/assets`
|
||||
const payload: AssetPayload = {
|
||||
filters: {
|
||||
address: contractAddress.toLowerCase(),
|
||||
traits: {},
|
||||
searchText,
|
||||
notForSale,
|
||||
tokenId,
|
||||
...rarityRange,
|
||||
},
|
||||
fields: {
|
||||
address: 1,
|
||||
name: 1,
|
||||
id: 1,
|
||||
imageUrl: 1,
|
||||
currentPrice: 1,
|
||||
currentUsdPrice: 1,
|
||||
paymentToken: 1,
|
||||
animationUrl: 1,
|
||||
notForSale: 1,
|
||||
rarity: 1,
|
||||
tokenId: 1,
|
||||
},
|
||||
limit: 25,
|
||||
offset: pageParam * 25,
|
||||
}
|
||||
if (sort) {
|
||||
payload.sort = sort
|
||||
}
|
||||
if (markets) {
|
||||
payload.markets = markets
|
||||
}
|
||||
const numberOfTraits = traits?.filter((trait) => trait.trait_type === 'Number of traits')
|
||||
if (numberOfTraits) {
|
||||
payload.filters.numTraits = numberOfTraits.map((el) => ({ traitCount: el.trait_value }))
|
||||
}
|
||||
if (traits) {
|
||||
payload.filters.traits = formatTraits(traits)
|
||||
}
|
||||
|
||||
const low = price?.low ? parseFloat(formatPrice(price.low)) : undefined
|
||||
const high = price?.high ? parseFloat(formatPrice(price.high)) : undefined
|
||||
|
||||
// Only consider sending eth price filters when searching
|
||||
// across listed assets
|
||||
if (!notForSale) {
|
||||
if (low || high) {
|
||||
payload.filters.currentEthPrice = {}
|
||||
}
|
||||
|
||||
if (low && payload.filters.currentEthPrice) {
|
||||
payload.filters.currentEthPrice.$gte = low
|
||||
}
|
||||
|
||||
if (high && payload.filters.currentEthPrice) {
|
||||
payload.filters.currentEthPrice.$lte = high
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
const data = await r.json()
|
||||
// Unfortunately have to include totalCount into each element. The fetcher
|
||||
// for swr infinite must return an array.
|
||||
for (const x of data.data) {
|
||||
x.totalCount = data.totalCount
|
||||
x.numTraitsByAmount = data.numTraitsByAmount
|
||||
}
|
||||
|
||||
// Uncomment the lines belo if you want to simulate a delay
|
||||
// await (async () => await new Promise((resolve) => setTimeout(resolve, 50000)))();
|
||||
|
||||
return data.data
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
import { isAddress } from '@ethersproject/address'
|
||||
import { groupBy } from 'nft/utils/groupBy'
|
||||
|
||||
import { GenieCollection } from '../../types'
|
||||
|
||||
export const CollectionStatsFetcher = async (addressOrName: string, recursive = false): Promise<GenieCollection> => {
|
||||
const isName = !isAddress(addressOrName.toLowerCase())
|
||||
const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/collections`
|
||||
|
||||
if (!isName && !recursive) {
|
||||
try {
|
||||
return await CollectionStatsFetcher(addressOrName.toLowerCase(), true)
|
||||
} catch {
|
||||
// Handle Error
|
||||
}
|
||||
}
|
||||
|
||||
const filters = isName
|
||||
? {
|
||||
$or: [{ name: { $regex: addressOrName, $options: 'i' } }],
|
||||
}
|
||||
: { address: addressOrName }
|
||||
|
||||
const payload = {
|
||||
filters,
|
||||
limit: isName ? 6 : 1,
|
||||
fields: isName
|
||||
? {
|
||||
name: 1,
|
||||
imageUrl: 1,
|
||||
address: 1,
|
||||
stats: 1,
|
||||
floorPrice: 1,
|
||||
}
|
||||
: {
|
||||
traits: 1,
|
||||
stats: 1,
|
||||
'indexingStats.openSea': 1,
|
||||
imageUrl: 1,
|
||||
bannerImageUrl: 1,
|
||||
twitter: 1,
|
||||
externalUrl: 1,
|
||||
instagram: 1,
|
||||
discordUrl: 1,
|
||||
marketplaceCount: 1,
|
||||
floorPrice: 1,
|
||||
},
|
||||
offset: 0,
|
||||
}
|
||||
const r = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
const data = await r.json()
|
||||
const collections = data?.data.map((collection: Record<string, unknown>) => {
|
||||
// @ts-ignore
|
||||
collection.stats.floor_price = collection.floorPrice
|
||||
|
||||
return {
|
||||
...collection,
|
||||
traits: collection.traits && groupBy(collection.traits as unknown[], 'trait_type'),
|
||||
}
|
||||
})
|
||||
return collections[0]
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
import { GenieCollection } from '../../types'
|
||||
|
||||
export const fetchMultipleCollectionStats = async ({
|
||||
addresses,
|
||||
}: {
|
||||
addresses: string[]
|
||||
}): Promise<GenieCollection[]> => {
|
||||
const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/searchCollections`
|
||||
const filters = {
|
||||
address: { $in: addresses },
|
||||
}
|
||||
const payload = {
|
||||
filters,
|
||||
fields: {
|
||||
stats: 1,
|
||||
imageUrl: 1,
|
||||
address: 1,
|
||||
name: 1,
|
||||
},
|
||||
}
|
||||
|
||||
const r = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
const data = await r.json()
|
||||
return data.data
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import { CollectionInfoForAsset, GenieAsset } from '../../types'
|
||||
|
||||
interface ReponseTrait {
|
||||
trait_type: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export const fetchSingleAsset = async ({
|
||||
contractAddress,
|
||||
tokenId,
|
||||
}: {
|
||||
contractAddress: string
|
||||
tokenId?: string
|
||||
}): Promise<[GenieAsset, CollectionInfoForAsset]> => {
|
||||
const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/assetDetails?address=${contractAddress}&tokenId=${tokenId}`
|
||||
const r = await fetch(url)
|
||||
const data = await r.json()
|
||||
const asset = data.asset[0]
|
||||
|
||||
asset.traits = asset.traits.map((trait: ReponseTrait) => ({ trait_type: trait.trait_type, trait_value: trait.value }))
|
||||
|
||||
return [asset, data.collection]
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
import { parseEther } from '@ethersproject/units'
|
||||
import { Trait } from 'nft/hooks/useCollectionFilters'
|
||||
import { AssetPayload, GenieAsset } from 'nft/types'
|
||||
|
||||
import { formatTraits } from './AssetsFetcher'
|
||||
|
||||
const formatPrice = (x: number | string) => parseEther(x.toString()).toString()
|
||||
|
||||
export const fetchSweep = async ({
|
||||
contractAddress,
|
||||
markets,
|
||||
price,
|
||||
rarityRange,
|
||||
traits,
|
||||
}: {
|
||||
contractAddress: string
|
||||
markets?: string[]
|
||||
price?: { high?: number | string; low?: number | string; symbol: string }
|
||||
rarityRange?: Record<string, unknown>
|
||||
traits?: Trait[]
|
||||
}): Promise<GenieAsset[]> => {
|
||||
const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/assets`
|
||||
const payload: AssetPayload = {
|
||||
filters: {
|
||||
address: contractAddress.toLowerCase(),
|
||||
traits: {},
|
||||
...rarityRange,
|
||||
},
|
||||
fields: {
|
||||
address: 1,
|
||||
name: 1,
|
||||
id: 1,
|
||||
imageUrl: 1,
|
||||
currentPrice: 1,
|
||||
currentUsdPrice: 1,
|
||||
paymentToken: 1,
|
||||
animationUrl: 1,
|
||||
notForSale: 1,
|
||||
rarity: 1,
|
||||
tokenId: 1,
|
||||
},
|
||||
limit: 50,
|
||||
offset: 0,
|
||||
}
|
||||
|
||||
if (markets) {
|
||||
payload.markets = markets
|
||||
}
|
||||
|
||||
if (traits) {
|
||||
payload.filters.traits = formatTraits(traits)
|
||||
}
|
||||
|
||||
const low = price?.low ? parseFloat(formatPrice(price.low)) : undefined
|
||||
const high = price?.high ? parseFloat(formatPrice(price.high)) : undefined
|
||||
|
||||
if (low || high) {
|
||||
payload.filters.currentEthPrice = {}
|
||||
}
|
||||
|
||||
if (low && payload.filters.currentEthPrice) {
|
||||
payload.filters.currentEthPrice.$gte = low
|
||||
}
|
||||
|
||||
if (high && payload.filters.currentEthPrice) {
|
||||
payload.filters.currentEthPrice.$lte = high
|
||||
}
|
||||
|
||||
const r = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
const data = await r.json()
|
||||
|
||||
return data.data
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { TransactionsResponse } from '../../types'
|
||||
|
||||
export const fetchTransactions = async (payload: { sweep?: boolean }): Promise<TransactionsResponse[]> => {
|
||||
const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/transactions`
|
||||
|
||||
const r = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
const data = (await r.json()) as TransactionsResponse[]
|
||||
|
||||
return data.filter(
|
||||
(x) => x.bannerImage && (payload.sweep ? x.nftCount >= 3 && Math.floor(x.ethValue / 10 ** 18) >= 1 : true)
|
||||
)
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { formatEther } from '@ethersproject/units'
|
||||
|
||||
import { WalletAsset } from '../../types'
|
||||
|
||||
const getEthPrice = (price: any) => {
|
||||
if (price.toString().includes('e')) {
|
||||
return BigNumber.from(10).pow(price.toString().split('e+')[1]).toString()
|
||||
}
|
||||
|
||||
return Math.round(price).toString()
|
||||
}
|
||||
|
||||
export const fetchWalletAssets = async ({
|
||||
ownerAddress,
|
||||
collectionAddresses,
|
||||
pageParam,
|
||||
}: {
|
||||
ownerAddress: string
|
||||
collectionAddresses?: string[]
|
||||
pageParam: number
|
||||
}): Promise<WalletAsset[]> => {
|
||||
const collectionAddressesString = collectionAddresses
|
||||
? collectionAddresses.reduce((str, collectionAddress) => str + `&assetContractAddresses=${collectionAddress}`, '')
|
||||
: ''
|
||||
const url = `${
|
||||
process.env.REACT_APP_GENIE_V3_API_URL
|
||||
}/walletAssets?address=${ownerAddress}${collectionAddressesString}&limit=25&offset=${pageParam * 25}`
|
||||
|
||||
const r = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
const data = await r.json()
|
||||
return data.data.assets.map((asset: any) => {
|
||||
return {
|
||||
...asset,
|
||||
collectionIsVerified: asset.asset_contract.isVerified,
|
||||
lastPrice: asset.last_sale.total_price && formatEther(asset.last_sale.total_price),
|
||||
floorPrice: asset.collection?.floorPrice,
|
||||
creatorPercentage: parseFloat(asset.asset_contract.dev_seller_fee_basis_points) / 10000,
|
||||
date_acquired: asset.last_sale ? asset.last_sale.event_timestamp : asset.asset_contract.created_date,
|
||||
listing_date: asset.sellOrders.length
|
||||
? Math.max
|
||||
.apply(
|
||||
null,
|
||||
asset.sellOrders.map(function (order: any) {
|
||||
return new Date(order.orderCreatedDate)
|
||||
})
|
||||
)
|
||||
.toString()
|
||||
: null,
|
||||
floor_sell_order_price: asset?.sellOrders?.length
|
||||
? Math.min(
|
||||
...asset.sellOrders.map((order: any) => {
|
||||
return parseFloat(formatEther(getEthPrice(order.ethPrice)))
|
||||
})
|
||||
)
|
||||
: null,
|
||||
}
|
||||
})
|
||||
}
|
@ -1,15 +1,7 @@
|
||||
export * from './ActivityFetcher'
|
||||
export * from './AssetsFetcher'
|
||||
export * from './CollectionPreviewFetcher'
|
||||
export * from './CollectionStatsFetcher'
|
||||
export * from './logListing'
|
||||
export * from './LooksRareRewardsFetcher'
|
||||
export * from './MultipleCollectionStatsFetcher'
|
||||
export * from './RouteFetcher'
|
||||
export * from './SearchCollectionsFetcher'
|
||||
export * from './SingleAssetFetcher'
|
||||
export * from './SweepFetcher'
|
||||
export * from './TransactionsFetcher'
|
||||
export * from './TrendingCollectionsFetcher'
|
||||
export * from './triggerPriceUpdatesForCollection'
|
||||
export * from './WalletAssetsFetcher'
|
||||
|
@ -1,13 +0,0 @@
|
||||
export const triggerPriceUpdatesForCollection = async (address: string) => {
|
||||
const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/collections/refresh`
|
||||
const r = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
address,
|
||||
}),
|
||||
})
|
||||
return r.json()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Deprecated_SellOrder, SellOrder } from '../sell'
|
||||
import { SellOrder } from '../sell'
|
||||
|
||||
export interface OpenSeaCollection {
|
||||
name: string
|
||||
@ -88,7 +88,7 @@ export interface GenieAsset {
|
||||
name?: string
|
||||
priceInfo: PriceInfo
|
||||
susFlag?: boolean
|
||||
sellorders?: Deprecated_SellOrder[] | SellOrder[] // TODO remove Deprecated_SellOrder when full migration to GraphQL is complete
|
||||
sellorders?: SellOrder[]
|
||||
smallImageUrl?: string
|
||||
tokenId: string
|
||||
tokenType: TokenType
|
||||
|
@ -12,24 +12,6 @@ export interface ListingWarning {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface Deprecated_SellOrder {
|
||||
assetId: string
|
||||
ethPrice: number
|
||||
basePrice: number
|
||||
baseCurrency: string
|
||||
baseCurrencyDecimal: number
|
||||
orderCreatedDate: string
|
||||
orderClosingDate: string
|
||||
quantity: number
|
||||
timestamp: string
|
||||
marketplace: string
|
||||
marketplaceUrl: string
|
||||
orderHash: string
|
||||
ammFeePercent?: number
|
||||
ethReserves?: number
|
||||
tokenReserves?: number
|
||||
}
|
||||
|
||||
export interface SellOrder {
|
||||
address: string
|
||||
createdAt: number
|
||||
@ -78,7 +60,7 @@ export interface WalletAsset {
|
||||
creatorPercentage: number
|
||||
listing_date: string
|
||||
date_acquired: string
|
||||
sellOrders: Deprecated_SellOrder[] | SellOrder[] // TODO remove Deprecated_SellOrder when full migration to GraphQL is complete
|
||||
sellOrders: SellOrder[]
|
||||
floor_sell_order_price: number
|
||||
// Used for creating new listings
|
||||
expirationTime?: number
|
||||
|
@ -1,13 +1,5 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import {
|
||||
BagItem,
|
||||
BagItemStatus,
|
||||
Deprecated_SellOrder,
|
||||
GenieAsset,
|
||||
Markets,
|
||||
SellOrder,
|
||||
UpdatedGenieAsset,
|
||||
} from 'nft/types'
|
||||
import { BagItem, BagItemStatus, GenieAsset, Markets, UpdatedGenieAsset } from 'nft/types'
|
||||
|
||||
// TODO: a lot of the below typecasting logic can be simplified when GraphQL migration is complete
|
||||
export const calcPoolPrice = (asset: GenieAsset, position = 0) => {
|
||||
@ -15,11 +7,7 @@ export const calcPoolPrice = (asset: GenieAsset, position = 0) => {
|
||||
let marginalBuy: BigNumber = BigNumber.from(0)
|
||||
if (!asset.sellorders) return ''
|
||||
|
||||
const nft =
|
||||
(asset.sellorders[0] as Deprecated_SellOrder).ammFeePercent === undefined
|
||||
? (asset.sellorders[0] as SellOrder).protocolParameters
|
||||
: (asset.sellorders[0] as Deprecated_SellOrder)
|
||||
|
||||
const nft = asset.sellorders[0].protocolParameters
|
||||
const decimals = BigNumber.from(1).mul(10).pow(18)
|
||||
const ammFee = nft?.ammFeePercent ? (100 + (nft.ammFeePercent as number)) * 100 : 110 * 100
|
||||
|
||||
@ -43,29 +31,23 @@ export const calcPoolPrice = (asset: GenieAsset, position = 0) => {
|
||||
|
||||
const ethReserves = BigNumber.from(
|
||||
(
|
||||
(nft?.ethReserves as number) ??
|
||||
(
|
||||
nft as Record<
|
||||
string,
|
||||
{
|
||||
ethReserves: number
|
||||
}
|
||||
>
|
||||
)?.poolMetadata?.ethReserves
|
||||
)?.toLocaleString('fullwide', { useGrouping: false }) ?? 1
|
||||
nft as Record<
|
||||
string,
|
||||
{
|
||||
ethReserves: number
|
||||
}
|
||||
>
|
||||
)?.poolMetadata?.ethReserves?.toLocaleString('fullwide', { useGrouping: false }) ?? 1
|
||||
)
|
||||
const tokenReserves = BigNumber.from(
|
||||
(
|
||||
(nft?.tokenReserves as number) ??
|
||||
(
|
||||
nft as Record<
|
||||
string,
|
||||
{
|
||||
tokenReserves: number
|
||||
}
|
||||
>
|
||||
)?.poolMetadata?.tokenReserves
|
||||
)?.toLocaleString('fullwide', { useGrouping: false }) ?? 1
|
||||
nft as Record<
|
||||
string,
|
||||
{
|
||||
tokenReserves: number
|
||||
}
|
||||
>
|
||||
)?.poolMetadata?.tokenReserves?.toLocaleString('fullwide', { useGrouping: false }) ?? 1
|
||||
)
|
||||
const numerator = ethReserves.mul(amountToBuy).mul(1000)
|
||||
const denominator = tokenReserves.sub(amountToBuy).mul(997)
|
||||
|
Loading…
Reference in New Issue
Block a user