Commit 9fd6ab01 authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

refactor: Asset Details GQL Query (#5037)

* add details query

* add new query file

* connect details gql query to page

* remove comment

* type refactoring

* working pooled asset logic

* useLazyload

* remove any cast

* Deprecated_SellOrder

* extranneous cast

* return markets
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
parent 48aa1140
......@@ -19,7 +19,7 @@ const nftHeaders = {
// The issue below prevented using a custom var in metadata to gate which queries are for the nft endpoint vs base endpoint
// This is a temporary solution before the two endpoints merge
// https://github.com/relay-tools/relay-hooks/issues/215
const NFT_QUERIES = ['AssetQuery', 'AssetPaginationQuery', 'CollectionQuery']
const NFT_QUERIES = ['AssetQuery', 'AssetPaginationQuery', 'CollectionQuery', 'DetailsQuery']
const fetchQuery = (params: RequestParameters, variables: Variables): Promise<GraphQLResponse> => {
const isNFT = NFT_QUERIES.includes(params.name)
......
import graphql from 'babel-plugin-relay/macro'
import { parseEther } from 'ethers/lib/utils'
import { GenieAsset, Rarity } from 'nft/types'
import { GenieAsset, Rarity, SellOrder } from 'nft/types'
import { useLazyLoadQuery, usePaginationFragment } from 'react-relay'
import { AssetPaginationQuery } from './__generated__/AssetPaginationQuery.graphql'
......@@ -75,6 +75,7 @@ const assetPaginationQuery = graphql`
taker
tokenId
type
protocolParameters
}
cursor
}
......@@ -155,7 +156,14 @@ export function useAssetsQuery(
}
: undefined,
susFlag: asset.suspiciousFlag,
sellorders: asset.listings?.edges,
sellorders: asset.listings?.edges.map((listingNode: { node: SellOrder }) => {
return {
...listingNode.node,
protocolParameters: listingNode.node.protocolParameters
? JSON.parse(listingNode.node.protocolParameters.toString())
: undefined,
}
}),
smallImageUrl: asset.smallImage?.url,
tokenId: asset.tokenId,
tokenType: asset.collection.nftContracts[0]?.standard,
......
import { parseEther } from '@ethersproject/units'
import graphql from 'babel-plugin-relay/macro'
import { Trait } from 'nft/hooks'
import { CollectionInfoForAsset, GenieAsset, SellOrder, TokenType } from 'nft/types'
import { useLazyLoadQuery } from 'react-relay'
import { DetailsQuery } from './__generated__/DetailsQuery.graphql'
const detailsQuery = graphql`
query DetailsQuery($address: String!, $tokenId: String!) {
nftAssets(address: $address, filter: { listed: false, tokenIds: [$tokenId] }) {
edges {
node {
id
name
ownerAddress
image {
url
}
smallImage {
url
}
originalImage {
url
}
tokenId
description
animationUrl
suspiciousFlag
creator {
address
profileImage {
url
}
isVerified
}
collection {
name
isVerified
numAssets
image {
url
}
nftContracts {
address
standard
}
description
}
listings(first: 1) {
edges {
node {
address
createdAt
endAt
id
maker
marketplace
marketplaceUrl
orderHash
price {
currency
value
}
quantity
startAt
status
taker
tokenId
type
protocolParameters
}
cursor
}
}
rarities {
provider
rank
score
}
metadataUrl
traits {
name
value
}
}
}
}
}
`
export function useDetailsQuery(address: string, tokenId: string): [GenieAsset, CollectionInfoForAsset] | undefined {
const queryData = useLazyLoadQuery<DetailsQuery>(detailsQuery, {
address,
tokenId,
})
const asset = queryData.nftAssets?.edges[0]?.node
const collection = asset?.collection
const ethPrice = parseEther(asset?.listings?.edges[0].node.price.value?.toString() ?? '0').toString()
return [
{
id: asset?.id,
address,
notForSale: asset?.listings === null,
collectionName: asset?.collection?.name ?? undefined,
collectionSymbol: asset?.collection?.image?.url,
imageUrl: asset?.image?.url,
animationUrl: asset?.animationUrl ?? undefined,
marketplace: asset?.listings?.edges[0]?.node.marketplace.toLowerCase() as any,
name: asset?.name ?? undefined,
priceInfo: {
ETHPrice: ethPrice,
baseAsset: 'ETH',
baseDecimals: '18',
basePrice: ethPrice,
},
susFlag: asset?.suspiciousFlag ?? undefined,
sellorders: asset?.listings?.edges.map((listingNode) => {
return {
...listingNode.node,
protocolParameters: listingNode.node.protocolParameters
? JSON.parse(listingNode.node.protocolParameters.toString())
: undefined,
} as SellOrder
}),
smallImageUrl: asset?.smallImage?.url,
tokenId,
tokenType: (asset?.collection?.nftContracts && asset?.collection.nftContracts[0]?.standard) as TokenType,
collectionIsVerified: asset?.collection?.isVerified ?? undefined,
rarity: {
primaryProvider: 'Rarity Sniper', // TODO update when backend adds more providers
providers: asset?.rarities
? asset?.rarities?.map((rarity) => {
return {
rank: rarity.rank ?? undefined,
score: rarity.score ?? undefined,
provider: 'Rarity Sniper',
}
})
: undefined,
},
owner: asset?.ownerAddress ?? undefined,
creator: {
profile_img_url: asset?.creator?.profileImage?.url,
address: asset?.creator?.address,
},
metadataUrl: asset?.metadataUrl ?? undefined,
traits: asset?.traits?.map((trait) => {
return { trait_type: trait.name ?? undefined, trait_value: trait.value ?? undefined } as Trait
}),
},
{
collectionDescription: collection?.description,
collectionImageUrl: collection?.image?.url,
collectionName: collection?.name ?? undefined,
isVerified: collection?.isVerified ?? undefined,
totalSupply: collection?.numAssets ?? undefined,
},
]
}
......@@ -577,7 +577,7 @@ const DetailsLink = () => {
/* -------- RANKING CARD -------- */
interface RankingProps {
rarity: Rarity
provider: { url?: string; rank: number }
provider: { url?: string; rank?: number }
rarityVerified: boolean
rarityLogo?: string
}
......@@ -586,6 +586,8 @@ const Ranking = ({ rarity, provider, rarityVerified, rarityLogo }: RankingProps)
const { asset } = useCardContext()
return (
<>
{provider.rank && (
<RankingContainer>
<MouseoverTooltip
text={
......@@ -613,6 +615,8 @@ const Ranking = ({ rarity, provider, rarityVerified, rarityLogo }: RankingProps)
</Box>
</MouseoverTooltip>
</RankingContainer>
)}
</>
)
}
const SUSPICIOUS_TEXT = 'Blocked on OpenSea'
......
......@@ -56,12 +56,14 @@ export const CollectionAsset = ({
let notForSale = true
let assetMediaType = AssetMediaType.Image
notForSale = asset.notForSale || BigNumber.from(asset.priceInfo.ETHPrice ? asset.priceInfo.ETHPrice : 0).lt(0)
notForSale = asset.notForSale || BigNumber.from(asset.priceInfo ? asset.priceInfo.ETHPrice : 0).lt(0)
if (asset.animationUrl) {
if (isAudio(asset.animationUrl)) {
assetMediaType = AssetMediaType.Audio
} else if (isVideo(asset.animationUrl)) {
assetMediaType = AssetMediaType.Video
}
}
return {
notForSale,
......@@ -71,7 +73,9 @@ export const CollectionAsset = ({
const { provider, rarityLogo } = useMemo(() => {
return {
provider: asset.rarity?.providers.find(({ provider: _provider }) => _provider === asset.rarity?.primaryProvider),
provider: asset?.rarity?.providers?.find(
({ provider: _provider }) => _provider === asset.rarity?.primaryProvider
),
rarityLogo: rarityProviderLogo[asset.rarity?.primaryProvider ?? 0] ?? '',
}
}, [asset])
......@@ -98,7 +102,7 @@ export const CollectionAsset = ({
>
<Card.ImageContainer>
{asset.tokenType === 'ERC1155' && quantity > 0 && <Card.Erc1155Controls quantity={quantity.toString()} />}
{asset.rarity && provider && provider.rank && (
{asset.rarity && provider && (
<Card.Ranking
rarity={asset.rarity}
provider={provider}
......
......@@ -16,7 +16,7 @@ import { badge, bodySmall, caption, headlineMedium, subhead } from 'nft/css/comm
import { themeVars } from 'nft/css/sprinkles.css'
import { useBag } from 'nft/hooks'
import { useTimeout } from 'nft/hooks/useTimeout'
import { CollectionInfoForAsset, GenieAsset, SellOrder } from 'nft/types'
import { CollectionInfoForAsset, Deprecated_SellOrder, GenieAsset, SellOrder } from 'nft/types'
import { useUsdPrice } from 'nft/utils'
import { shortenAddress } from 'nft/utils/address'
import { formatEthPrice } from 'nft/utils/currency'
......@@ -60,9 +60,9 @@ const AudioPlayer = ({
const formatter = Intl.DateTimeFormat('en-GB', { dateStyle: 'full', timeStyle: 'short' })
const CountdownTimer = ({ sellOrder }: { sellOrder: SellOrder }) => {
const CountdownTimer = ({ sellOrder }: { sellOrder: Deprecated_SellOrder | SellOrder }) => {
const { date, expires } = useMemo(() => {
const date = new Date(sellOrder.orderClosingDate)
const date = new Date((sellOrder as Deprecated_SellOrder).orderClosingDate ?? (sellOrder as SellOrder).endAt)
return {
date,
expires: formatter.format(date),
......@@ -148,7 +148,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
() =>
asset.rarity
? {
rarityProvider: asset.rarity.providers.find(
rarityProvider: asset?.rarity?.providers?.find(
({ provider: _provider }) => _provider === asset.rarity?.primaryProvider
),
rarityLogo: rarityProviderLogo[asset.rarity.primaryProvider] || '',
......@@ -158,16 +158,16 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
)
const assetMediaType = useMemo(() => {
if (isAudio(asset.animationUrl)) {
if (isAudio(asset.animationUrl ?? '')) {
return MediaType.Audio
} else if (isVideo(asset.animationUrl)) {
} else if (isVideo(asset.animationUrl ?? '')) {
return MediaType.Video
}
return MediaType.Image
}, [asset])
useEffect(() => {
if (asset.creator) setCreatorAddress(asset.creator.address)
if (asset.creator) setCreatorAddress(asset.creator.address ?? '')
if (asset.owner) setOwnerAddress(asset.owner)
}, [asset])
......@@ -355,7 +355,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
</Row>
</Column>
{asset.priceInfo && !isOwned ? (
{asset.priceInfo && asset.sellorders && !isOwned ? (
<Row
marginTop="8"
marginBottom="40"
......@@ -372,7 +372,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
<a href={asset.sellorders[0].marketplaceUrl} rel="noreferrer" target="_blank">
<img
className={styles.marketplace}
src={`/nft/svgs/marketplaces/${asset.sellorders[0].marketplace}.svg`}
src={`/nft/svgs/marketplaces/${asset.sellorders[0].marketplace.toLowerCase()}.svg`}
height={16}
width={16}
alt="Markeplace"
......@@ -387,7 +387,10 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
</Box>
)}
</Row>
{asset.sellorders?.[0].orderClosingDate ? <CountdownTimer sellOrder={asset.sellorders[0]} /> : null}
{(asset.sellorders?.[0] as Deprecated_SellOrder).orderClosingDate ||
(asset.sellorders?.[0] as SellOrder).endAt ? (
<CountdownTimer sellOrder={asset.sellorders[0]} />
) : null}
</Column>
<Box
as="button"
......
......@@ -4,7 +4,7 @@ import { EventName, PageName } from 'analytics/constants'
import { useTrace } from 'analytics/Trace'
import { CancelListingIcon, MinusIcon, PlusIcon } from 'nft/components/icons'
import { useBag } from 'nft/hooks'
import { CollectionInfoForAsset, GenieAsset, TokenType } from 'nft/types'
import { CollectionInfoForAsset, Deprecated_SellOrder, GenieAsset, SellOrder, TokenType } from 'nft/types'
import { ethNumberStandardFormatter, formatEthPrice, getMarketplaceIcon, timeLeft, useUsdPrice } from 'nft/utils'
import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
......@@ -113,7 +113,9 @@ const DiscoveryContainer = styled.div`
export const OwnerContainer = ({ asset }: { asset: GenieAsset }) => {
const listing = asset.sellorders && asset.sellorders.length > 0 ? asset.sellorders[0] : undefined
const expirationDate = listing ? new Date(listing.orderClosingDate) : undefined
const expirationDate = listing
? new Date((listing as Deprecated_SellOrder).orderClosingDate ?? (listing as SellOrder).endAt)
: undefined
const USDPrice = useUsdPrice(asset)
const navigate = useNavigate()
......@@ -191,7 +193,9 @@ export const NotForSale = ({ collection }: { collection: CollectionInfoForAsset
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.orderClosingDate) : undefined
const expirationDate = cheapestOrder
? new Date((cheapestOrder as Deprecated_SellOrder).orderClosingDate ?? (cheapestOrder as SellOrder).endAt)
: undefined
const itemsInBag = useBag((s) => s.itemsInBag)
const addAssetsToBag = useBag((s) => s.addAssetsToBag)
const removeAssetsFromBag = useBag((s) => s.removeAssetsFromBag)
......
......@@ -12,8 +12,8 @@ export const CollectionProfile = ({
}: {
isVerified?: boolean
label: string
name: string
avatarUrl: string
name?: string
avatarUrl?: string
} & BoxProps) => {
return (
<Row {...props}>
......
......@@ -23,9 +23,9 @@ export const Details = ({
}: {
contractAddress: string
tokenId: string
metadataUrl: string
metadataUrl?: string
tokenType: string
totalSupply: number
totalSupply?: number
blockchain: string
}) => (
<Row gap={{ md: '32', sm: '16' }} width="full" justifyContent="space-between" alignItems="flex-start" flexWrap="wrap">
......
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'
......@@ -16,6 +18,8 @@ const AssetContainer = styled.div`
const Asset = () => {
const { tokenId = '', contractAddress = '' } = useParams()
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
const { data } = useQuery(
['assetDetail', contractAddress, tokenId],
() => fetchSingleAsset({ contractAddress, tokenId }),
......@@ -25,9 +29,13 @@ const Asset = () => {
refetchOnReconnect: false,
}
)
const gqlData = useDetailsQuery(contractAddress, tokenId)
const asset = useMemo(() => (data ? data[0] : undefined), [data])
const collection = useMemo(() => (data ? data[1] : undefined), [data])
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]
)
return (
<>
......
......@@ -52,7 +52,7 @@ const buildRouteItem = (item: GenieAsset): RouteItem => {
return {
id: item.id,
symbol: item.priceInfo.baseAsset,
name: item.name,
name: item.name ?? '',
decimals: parseFloat(item.priceInfo.baseDecimals),
address: item.address,
tokenType: item.tokenType,
......
......@@ -21,12 +21,11 @@ export interface AssetPayload {
}
export interface CollectionInfoForAsset {
collectionSymbol: string
collectionDescription: string | null
collectionImageUrl: string
collectionName: string
isVerified: boolean
totalSupply: number
collectionDescription?: string | null
collectionImageUrl?: string
collectionName?: string
isVerified?: boolean
totalSupply?: number
}
export type CollectionSort = Record<
......
import { Trait } from 'nft/hooks/useCollectionFilters'
import { SellOrder } from '../sell'
import { Deprecated_SellOrder, SellOrder } from '../sell'
export interface OpenSeaCollection {
name: string
......@@ -45,9 +45,9 @@ export interface OpenSeaAsset {
interface OpenSeaUser {
user?: null
profile_img_url: string
address: string
config: string
profile_img_url?: string
address?: string
config?: string
}
export enum TokenType {
......@@ -60,7 +60,7 @@ export enum TokenType {
export interface PriceInfo {
ETHPrice: string
USDPrice: string
USDPrice?: string
baseAsset: string
baseDecimals: string
basePrice: string
......@@ -74,31 +74,31 @@ export interface AssetSellOrder {
export interface Rarity {
primaryProvider: string
providers: { provider: string; rank: number; url?: string; score: number }[]
providers?: { provider: string; rank?: number; url?: string; score?: number }[]
}
export interface GenieAsset {
id?: string // This would be a random id created and assigned by front end
address: string
notForSale: boolean
collectionName: string
collectionSymbol: string
imageUrl: string
animationUrl: string
marketplace: Markets
name: string
collectionName?: string
collectionSymbol?: string
imageUrl?: string
animationUrl?: string
marketplace?: Markets
name?: string
priceInfo: PriceInfo
susFlag: boolean
sellorders: SellOrder[]
smallImageUrl: string
susFlag?: boolean
sellorders?: Deprecated_SellOrder[] | SellOrder[] // TODO remove OldSellOrder when full migration to GraphQL is complete
smallImageUrl?: string
tokenId: string
tokenType: TokenType
totalCount?: number // The totalCount from the query to /assets
collectionIsVerified?: boolean
rarity?: Rarity
owner: string
owner?: string
creator: OpenSeaUser
metadataUrl: string
metadataUrl?: string
traits?: Trait[]
}
......
import { NftMarketplace, OrderStatus, OrderType } from 'graphql/data/nft/__generated__/DetailsQuery.graphql'
import { GenieCollection } from '../common'
export interface ListingMarket {
......@@ -10,7 +12,7 @@ export interface ListingWarning {
message: string
}
export interface SellOrder {
export interface Deprecated_SellOrder {
assetId: string
ethPrice: number
basePrice: number
......@@ -28,6 +30,27 @@ export interface SellOrder {
tokenReserves?: number
}
export interface SellOrder {
address: string
createdAt: number
endAt: number
id: string
maker: string
marketplace: NftMarketplace
marketplaceUrl: string
orderHash: string
price: {
currency: string
value: number
}
quantity: number
startAt: number
status: OrderStatus
tokenId: string
type: OrderType
protocolParameters: Record<string, unknown>
}
export interface Listing {
price?: number
marketplace: ListingMarket
......@@ -61,7 +84,7 @@ export interface WalletAsset {
creatorPercentage: number
listing_date: string
date_acquired: string
sellOrders: SellOrder[]
sellOrders: Deprecated_SellOrder[]
floor_sell_order_price: number
// Used for creating new listings
expirationTime?: number
......
import { BigNumber } from '@ethersproject/bignumber'
import { BagItem, BagItemStatus, GenieAsset, Markets, UpdatedGenieAsset } from 'nft/types'
import {
BagItem,
BagItemStatus,
Deprecated_SellOrder,
GenieAsset,
Markets,
SellOrder,
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) => {
let amountToBuy: BigNumber = BigNumber.from(0)
let marginalBuy: BigNumber = BigNumber.from(0)
const nft = asset.sellorders[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 decimals = BigNumber.from(1).mul(10).pow(18)
const ammFee = nft.ammFeePercent ? (100 + nft.ammFeePercent) * 100 : 110 * 100
const ammFee = nft.ammFeePercent ? (100 + (nft.ammFeePercent as number)) * 100 : 110 * 100
if (asset.marketplace === Markets.NFTX) {
const sixteenmul = BigNumber.from(1).mul(10).pow(16)
......@@ -26,8 +41,32 @@ export const calcPoolPrice = (asset: GenieAsset, position = 0) => {
marginalBuy = marginalBuy.mul(decimals)
}
const ethReserves = BigNumber.from(nft.ethReserves?.toLocaleString('fullwide', { useGrouping: false }))
const tokenReserves = BigNumber.from(nft.tokenReserves?.toLocaleString('fullwide', { useGrouping: false }))
const ethReserves = BigNumber.from(
(
(nft.ethReserves as number) ??
(
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
)
const numerator = ethReserves.mul(amountToBuy).mul(1000)
const denominator = tokenReserves.sub(amountToBuy).mul(997)
......@@ -71,7 +110,7 @@ export const recalculateBagUsingPooledAssets = (uncheckedItemsInBag: BagItem[])
const possibleMarkets = itemsInBag.reduce((markets, item) => {
const asset = item.asset
const market = asset.marketplace
if (!isPooledMarket(market)) return markets
if (!market || !isPooledMarket(market)) return markets
const key = asset.address + asset.marketplace
if (Object.keys(markets).includes(key)) {
......@@ -85,8 +124,7 @@ export const recalculateBagUsingPooledAssets = (uncheckedItemsInBag: BagItem[])
const updatedPriceMarkets = itemsInBag.reduce((markets, item) => {
const asset = item.asset
const market = asset.marketplace
if (!asset.updatedPriceInfo) return markets
if (!isPooledMarket(market)) return markets
if (!market || !asset.updatedPriceInfo || !isPooledMarket(market)) return markets
const key = asset.address + asset.marketplace
if (Object.keys(markets).includes(key)) {
......@@ -103,6 +141,7 @@ export const recalculateBagUsingPooledAssets = (uncheckedItemsInBag: BagItem[])
}, {} as { [key: string]: string })
itemsInBag.forEach((item) => {
if (item.asset.marketplace)
if (isPooledMarket(item.asset.marketplace)) {
const asset = item.asset
const isPriceChangedAsset = !!asset.updatedPriceInfo
......
......@@ -18,7 +18,9 @@ export const fetchPrice = async (currency: Currency = Currency.ETH): Promise<num
}
}
export function useUsdPrice(asset: GenieAsset): string {
export function useUsdPrice(asset: GenieAsset): string | undefined {
const { data: fetchedPriceData } = useQuery(['fetchPrice', {}], () => fetchPrice(), {})
return fetchedPriceData ? (parseFloat(formatEther(asset.priceInfo.ETHPrice)) * fetchedPriceData).toString() : ''
return fetchedPriceData && asset.priceInfo.ETHPrice
? (parseFloat(formatEther(asset.priceInfo.ETHPrice)) * fetchedPriceData).toString()
: undefined
}
......@@ -6,7 +6,6 @@ import TopLevelModals from 'components/TopLevelModals'
import { useFeatureFlagsIsLoaded } from 'featureFlags'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
import { CollectionPageSkeleton } from 'nft/components/collection/CollectionPageSkeleton'
import { lazy, Suspense, useEffect, useState } from 'react'
import { Navigate, Route, Routes, useLocation } from 'react-router-dom'
import { useIsDarkMode } from 'state/user/hooks'
......@@ -245,23 +244,16 @@ export default function App() {
<>
<Route path="/profile" element={<Profile />} />
<Route path="/nfts" element={<NftExplore />} />
<Route path="/nfts/asset/:contractAddress/:tokenId" element={<Asset />} />
<Route
path="/nfts/collection/:contractAddress"
path="/nfts/asset/:contractAddress/:tokenId"
element={
<Suspense fallback={<CollectionPageSkeleton />}>
<Collection />
</Suspense>
}
/>
<Route
path="/nfts/collection/:contractAddress/activity"
element={
<Suspense fallback={<CollectionPageSkeleton />}>
<Collection />
<Suspense fallback={<div>Holder for loading ...</div>}>
<Asset />
</Suspense>
}
/>
<Route path="/nfts/collection/:contractAddress" element={<Collection />} />
<Route path="/nfts/collection/:contractAddress/activity" element={<Collection />} />
</>
)}
</Routes>
......
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