Commit 4e7b8264 authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

refactor: Wallet Assets GraphQL Query (#5053)

* begin nft balance integration

* inf scroll works

* working list

* update comment

* connect collection filters

* use lazyload

* update schema

* working with new schema

* details for nfs assets

* cleanup

* more null checks

* unique index for loading cards
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
parent 0ef6d162
......@@ -19,7 +19,14 @@ 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', 'DetailsQuery']
const NFT_QUERIES = [
'AssetQuery',
'AssetPaginationQuery',
'CollectionQuery',
'DetailsQuery',
'NftBalanceQuery',
'NftBalancePaginationQuery',
]
const fetchQuery = (params: RequestParameters, variables: Variables): Promise<GraphQLResponse> => {
const isNFT = NFT_QUERIES.includes(params.name)
......
......@@ -159,7 +159,7 @@ export function useAssetsQuery(
).toString()
return {
id: asset.id,
address: asset.collection.nftContracts[0]?.address,
address: asset.collection?.nftContracts[0]?.address,
notForSale: asset.listings?.edges.length === 0,
collectionName: asset.collection?.name,
collectionSymbol: asset.collection?.image?.url,
......@@ -186,7 +186,7 @@ export function useAssetsQuery(
}),
smallImageUrl: asset.smallImage?.url,
tokenId: asset.tokenId,
tokenType: asset.collection.nftContracts[0]?.standard,
tokenType: asset.collection?.nftContracts[0]?.standard,
// totalCount?: number, // TODO waiting for BE changes
collectionIsVerified: asset.collection?.isVerified,
rarity: {
......
......@@ -105,7 +105,7 @@ export function useCollectionQuery(address: string): GenieCollection | undefined
name: queryCollection?.name ?? undefined,
description: queryCollection?.description ?? undefined,
standard: queryCollection?.nftContracts ? queryCollection?.nftContracts[0]?.standard ?? undefined : undefined,
bannerImageUrl: queryCollection?.bannerImage?.url,
bannerImageUrl: queryCollection?.bannerImage?.url ?? undefined,
stats: queryCollection?.markets
? {
num_owners: market?.owners ?? undefined,
......
......@@ -97,7 +97,7 @@ export function useDetailsQuery(address: string, tokenId: string): [GenieAsset,
const asset = queryData.nftAssets?.edges[0]?.node
const collection = asset?.collection
const listing = asset?.listings?.edges[0]?.node
const ethPrice = parseEther(listing?.price.value?.toString() ?? '0').toString()
const ethPrice = parseEther(listing?.price?.value?.toString() ?? '0').toString()
return [
{
......@@ -105,8 +105,8 @@ export function useDetailsQuery(address: string, tokenId: string): [GenieAsset,
address,
notForSale: asset?.listings === null,
collectionName: asset?.collection?.name ?? undefined,
collectionSymbol: asset?.collection?.image?.url,
imageUrl: asset?.image?.url,
collectionSymbol: asset?.collection?.image?.url ?? undefined,
imageUrl: asset?.image?.url ?? undefined,
animationUrl: asset?.animationUrl ?? undefined,
// todo: fix the back/frontend discrepency here and drop the any
marketplace: listing?.marketplace.toLowerCase() as any,
......@@ -126,7 +126,7 @@ export function useDetailsQuery(address: string, tokenId: string): [GenieAsset,
: undefined,
} as SellOrder
}),
smallImageUrl: asset?.smallImage?.url,
smallImageUrl: asset?.smallImage?.url ?? undefined,
tokenId,
tokenType: (asset?.collection?.nftContracts && asset?.collection.nftContracts[0]?.standard) as TokenType,
collectionIsVerified: asset?.collection?.isVerified ?? undefined,
......@@ -153,8 +153,8 @@ export function useDetailsQuery(address: string, tokenId: string): [GenieAsset,
}),
},
{
collectionDescription: collection?.description,
collectionImageUrl: collection?.image?.url,
collectionDescription: collection?.description ?? undefined,
collectionImageUrl: collection?.image?.url ?? undefined,
collectionName: collection?.name ?? undefined,
isVerified: collection?.isVerified ?? undefined,
totalSupply: collection?.numAssets ?? undefined,
......
import graphql from 'babel-plugin-relay/macro'
import { WalletAsset } from 'nft/types'
import { useLazyLoadQuery, usePaginationFragment } from 'react-relay'
import { NftBalancePaginationQuery } from './__generated__/NftBalancePaginationQuery.graphql'
import { NftBalanceQuery } from './__generated__/NftBalanceQuery.graphql'
const nftBalancePaginationQuery = graphql`
fragment NftBalanceQuery_nftBalances on Query @refetchable(queryName: "NftBalancePaginationQuery") {
nftBalances(
ownerAddress: $ownerAddress
filter: $filter
first: $first
after: $after
last: $last
before: $before
) @connection(key: "NftBalanceQuery_nftBalances") {
edges {
node {
ownedAsset {
animationUrl
collection {
isVerified
image {
url
}
name
nftContracts {
address
chain
name
standard
symbol
totalSupply
}
markets(currencies: ETH) {
floorPrice {
value
}
}
}
description
flaggedBy
image {
url
}
originalImage {
url
}
name
ownerAddress
smallImage {
url
}
suspiciousFlag
tokenId
thumbnail {
url
}
listings(first: 1) {
edges {
node {
price {
value
currency
}
createdAt
marketplace
}
}
}
}
listedMarketplaces
listingFees {
payoutAddress
basisPoints
}
lastPrice {
currency
timestamp
value
}
}
}
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
`
const nftBalanceQuery = graphql`
query NftBalanceQuery(
$ownerAddress: String!
$filter: NftBalancesFilterInput
$first: Int
$after: String
$last: Int
$before: String
) {
...NftBalanceQuery_nftBalances
}
`
export function useNftBalanceQuery(
ownerAddress: string,
collectionFilters?: string[],
first?: number,
after?: string,
last?: number,
before?: string
) {
const queryData = useLazyLoadQuery<NftBalanceQuery>(nftBalanceQuery, {
ownerAddress,
filter: {
addresses: collectionFilters,
},
first,
after,
last,
before,
})
const { data, hasNext, loadNext, isLoadingNext } = usePaginationFragment<NftBalancePaginationQuery, any>(
nftBalancePaginationQuery,
queryData
)
const walletAssets: WalletAsset[] = data.nftBalances?.edges?.map((queryAsset: { node: any }) => {
const asset = queryAsset.node.ownedAsset
return {
id: asset.id,
image_url: asset.image?.url,
image_preview_url: asset.smallImage?.url,
name: asset.name,
tokenId: asset.tokenId,
asset_contract: {
address: asset.collection?.nftContracts[0].address,
schema_name: asset.collection?.nftContracts[0].standard,
name: asset.collection?.name,
description: asset.description,
image_url: asset.collection?.image?.url,
payout_address: queryAsset.node.listingFees && queryAsset.node.listingFees[0].payoutAddress,
},
collection: asset.collection,
collectionIsVerified: asset.collection?.isVerified,
lastPrice: queryAsset.node.lastPrice?.value,
floorPrice: asset.collection?.markets[0]?.floorPrice?.value,
creatorPercentage: queryAsset.node.listingFees && queryAsset.node.listingFees[0].basisPoints / 10000,
listing_date: asset.listings ? asset.listings[0]?.edges[0]?.node.createdAt : undefined,
date_acquired: queryAsset.node.lastPrice?.timestamp,
sellOrders: asset.listings?.edges,
floor_sell_order_price: asset.listings ? asset.listings[0]?.edges[0]?.node.price.value : undefined,
}
})
return { walletAssets, hasNext, isLoadingNext, loadNext }
}
......@@ -152,7 +152,13 @@ export const LoadingButton = styled.div`
export const DEFAULT_ASSET_QUERY_AMOUNT = 25
const loadingAssets = <>{new Array(DEFAULT_ASSET_QUERY_AMOUNT).fill(<CollectionAssetLoading />)}</>
const loadingAssets = (
<>
{Array.from(Array(DEFAULT_ASSET_QUERY_AMOUNT), (_, index) => (
<CollectionAssetLoading key={index} />
))}
</>
)
export const CollectionNftsLoading = () => (
<Box width="full" className={styles.assetList}>
......
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'
import { FilterButton } from 'nft/components/collection/FilterButton'
......@@ -15,8 +17,8 @@ import {
useWalletCollections,
} from 'nft/hooks'
import { ScreenBreakpointsPaddings } from 'nft/pages/collection/index.css'
import { fetchMultipleCollectionStats, fetchWalletAssets, OSCollectionsFetcher } from 'nft/queries'
import { ProfilePageStateType, WalletCollection } from 'nft/types'
import { fetchWalletAssets, OSCollectionsFetcher } from 'nft/queries'
import { ProfilePageStateType, WalletAsset, WalletCollection } from 'nft/types'
import { Dispatch, SetStateAction, useEffect, useMemo, useReducer, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component'
import { useInfiniteQuery, useQuery } from 'react-query'
......@@ -26,7 +28,7 @@ import styled from 'styled-components/macro'
import { EmptyWalletContent } from './EmptyWalletContent'
import { ProfileAccountDetails } from './ProfileAccountDetails'
import * as styles from './ProfilePage.css'
import { ProfilePageLoadingSkeleton } from './ProfilePageLoadingSkeleton'
import { ProfileBodyLoadingSkeleton } from './ProfilePageLoadingSkeleton'
import { WalletAssetDisplay } from './WalletAssetDisplay'
const SellModeButton = styled.button<{ active: boolean }>`
......@@ -52,13 +54,10 @@ const ProfilePageColumn = styled(Column)`
${ScreenBreakpointsPaddings}
`
export const DEFAULT_WALLET_ASSET_QUERY_AMOUNT = 25
const FILTER_SIDEBAR_WIDTH = 300
const PADDING = 16
function roundFloorPrice(price?: number, n?: number) {
return price ? Math.round(price * Math.pow(10, n ?? 3) + Number.EPSILON) / Math.pow(10, n ?? 3) : 0
}
export const ProfilePage = () => {
const { address } = useWalletBalance()
const collectionFilters = useWalletCollections((state) => state.collectionFilters)
......@@ -66,7 +65,6 @@ export const ProfilePage = () => {
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
const walletAssets = useWalletCollections((state) => state.walletAssets)
const setWalletAssets = useWalletCollections((state) => state.setWalletAssets)
const displayAssets = useWalletCollections((state) => state.displayAssets)
const setDisplayAssets = useWalletCollections((state) => state.setDisplayAssets)
const walletCollections = useWalletCollections((state) => state.walletCollections)
const setWalletCollections = useWalletCollections((state) => state.setWalletCollections)
......@@ -77,6 +75,7 @@ export const ProfilePage = () => {
const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded()
const isMobile = useIsMobile()
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
const [isSellMode, toggleSellMode] = useReducer((s) => !s, false)
const handleSellModeClick = () => {
......@@ -92,15 +91,6 @@ export const ProfilePage = () => {
}
)
const ownerCollectionsAddresses = useMemo(() => ownerCollections?.map(({ address }) => address), [ownerCollections])
const { data: collectionStats, isLoading: collectionStatsAreLoading } = useQuery(
['ownerCollectionStats', ownerCollectionsAddresses],
() => fetchMultipleCollectionStats({ addresses: ownerCollectionsAddresses ?? [] }),
{
refetchOnWindowFocus: false,
}
)
const {
data: ownerAssetsData,
fetchNextPage,
......@@ -118,60 +108,47 @@ export const ProfilePage = () => {
},
{
getNextPageParam: (lastPage, pages) => {
return lastPage?.flat().length === 25 ? pages.length : null
return lastPage?.flat().length === DEFAULT_WALLET_ASSET_QUERY_AMOUNT ? pages.length : null
},
refetchOnWindowFocus: false,
refetchOnMount: false,
}
)
const anyQueryIsLoading = collectionsAreLoading || collectionStatsAreLoading || assetsAreLoading
const anyQueryIsLoading = collectionsAreLoading || assetsAreLoading
const ownerAssets = useMemo(() => (isSuccess ? ownerAssetsData?.pages.flat() : null), [isSuccess, ownerAssetsData])
const {
walletAssets: gqlWalletAssets,
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(() => {
setDisplayAssets(walletAssets, listFilter)
}, [walletAssets, listFilter, setDisplayAssets])
!isNftGraphQl && setWalletAssets(ownerAssets?.flat() ?? [])
}, [ownerAssets, setWalletAssets, isNftGraphQl])
useEffect(() => {
setWalletAssets(ownerAssets?.flat() ?? [])
}, [ownerAssets, setWalletAssets])
!isNftGraphQl && setDisplayAssets(walletAssets, listFilter)
}, [walletAssets, listFilter, setDisplayAssets, isNftGraphQl])
useEffect(() => {
ownerCollections && setWalletCollections(ownerCollections)
}, [ownerCollections, setWalletCollections])
useEffect(() => {
if (ownerCollections?.length && collectionStats?.length) {
const ownerCollectionsCopy = [...ownerCollections]
for (const collection of ownerCollectionsCopy) {
const floorPrice = collectionStats.find((stat) => stat.address === collection.address)?.stats?.floor_price
collection.floorPrice = roundFloorPrice(floorPrice)
}
setWalletCollections(ownerCollectionsCopy)
}
}, [collectionStats, ownerCollections, setWalletCollections])
useEffect(() => {
if (ownerCollections?.length && collectionStats?.length) {
const ownerCollectionsCopy = [...ownerCollections]
for (const collection of ownerCollectionsCopy) {
const floorPrice = collectionStats.find((stat) => stat.address === collection.address)?.stats?.floor_price //TODO update when changing walletStats endpoint to gql
collection.floorPrice = floorPrice ? Math.round(floorPrice * 1000 + Number.EPSILON) / 1000 : 0 //round to at most 3 digits
}
setWalletCollections(ownerCollectionsCopy)
}
}, [collectionStats, ownerCollections, setWalletCollections])
const { gridX } = useSpring({
gridX: isFiltersExpanded ? FILTER_SIDEBAR_WIDTH : -PADDING,
})
return (
<ProfilePageColumn width="full" paddingTop={{ sm: `${PADDING}`, md: '40' }}>
{anyQueryIsLoading ? (
<ProfilePageLoadingSkeleton />
) : walletAssets.length === 0 ? (
{anyQueryIsLoading && !isNftGraphQl ? (
<ProfileBodyLoadingSkeleton />
) : ownerAssets?.length === 0 ? (
<EmptyWalletContent />
) : (
<Row alignItems="flex-start" position="relative">
......@@ -196,7 +173,7 @@ export const ProfilePage = () => {
onClick={() => setFiltersExpanded(!isFiltersExpanded)}
/>
<Row gap="8" flexWrap="nowrap">
{isSellMode && <SelectAllButton />}
{isSellMode && <SelectAllButton ownerAssets={ownerAssets ?? []} />}
<SellModeButton className={buttonTextMedium} active={isSellMode} onClick={handleSellModeClick}>
<TagIcon height={20} width={20} />
Sell
......@@ -212,21 +189,19 @@ export const ProfilePage = () => {
/>
</Row>
<InfiniteScroll
next={fetchNextPage}
hasMore={hasNextPage ?? false}
next={() => (isNftGraphQl ? loadNext(DEFAULT_WALLET_ASSET_QUERY_AMOUNT) : fetchNextPage())}
hasMore={isNftGraphQl ? hasNext : hasNextPage ?? false}
loader={
hasNextPage ? (
<Center>
<LoadingSparkle />
</Center>
) : null
<Center>
<LoadingSparkle />
</Center>
}
dataLength={displayAssets.length}
dataLength={ownerAssets?.length ?? 0}
style={{ overflow: 'unset' }}
>
<div className={assetList}>
{displayAssets && displayAssets.length
? displayAssets.map((asset, index) => (
{ownerAssets?.length
? ownerAssets.map((asset, index) => (
<WalletAssetDisplay asset={asset} isSellMode={isSellMode} key={index} />
))
: null}
......@@ -282,15 +257,21 @@ export const ProfilePage = () => {
)
}
const SelectAllButton = () => {
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) {
displayAssets.forEach((asset) => selectSellAsset(asset))
allAssets.forEach((asset) => selectSellAsset(asset))
} else {
resetSellAssets()
}
......
import { assetList } from 'nft/components/collection/CollectionNfts.css'
import styled from 'styled-components/macro'
import { DEFAULT_WALLET_ASSET_QUERY_AMOUNT } from './ProfilePage'
const SkeletonPageWrapper = styled.div`
padding: 40px 72px 52px;
width: 100%;
`
const SkeletonBodyWrapper = styled.div`
display: flex;
flex-direction: column;
width: 100%;
......@@ -64,9 +71,9 @@ export const ProfileAssetCardSkeleton = styled.div`
border-radius: 20px;
`
export const ProfilePageLoadingSkeleton = () => {
export const ProfileBodyLoadingSkeleton = () => {
return (
<SkeletonPageWrapper>
<SkeletonBodyWrapper>
<AccountDetailsSkeletonWrapper>
<ProfilePictureSkeleton />
<ProfileDetailsSkeleton />
......@@ -76,10 +83,18 @@ export const ProfilePageLoadingSkeleton = () => {
<SellButtonSkeleton />
</FilterBarSkeletonWrapper>
<div className={assetList}>
{new Array(25).map((_, index) => (
{Array.from(Array(DEFAULT_WALLET_ASSET_QUERY_AMOUNT), (_, index) => (
<ProfileAssetCardSkeleton key={index} />
))}
</div>
</SkeletonBodyWrapper>
)
}
export const ProfilePageLoadingSkeleton = () => {
return (
<SkeletonPageWrapper>
<ProfileBodyLoadingSkeleton />
</SkeletonPageWrapper>
)
}
......@@ -26,7 +26,9 @@ export const WalletAssetDisplay = ({ asset, isSellMode }: { asset: WalletAsset;
}, false)
const isSelected = useMemo(() => {
return sellAssets.some((item) => asset.id === item.id)
return sellAssets.some(
(item) => item.tokenId === asset.tokenId && item.asset_contract.address === asset.asset_contract.address
)
}, [asset, sellAssets])
const handleSelect = () => {
......@@ -42,7 +44,7 @@ export const WalletAssetDisplay = ({ asset, isSellMode }: { asset: WalletAsset;
}
const uniqueSellOrdersMarketplaces = useMemo(
() => [...new Set(asset.sellOrders.map((order) => order.marketplace))],
() => [...new Set(asset.sellOrders?.map((order) => order.marketplace))],
[asset.sellOrders]
)
......@@ -77,7 +79,7 @@ export const WalletAssetDisplay = ({ asset, isSellMode }: { asset: WalletAsset;
{asset.collectionIsVerified ? <VerifiedIcon className={styles.verifiedBadge} /> : null}
</Box>
</Column>
{asset.sellOrders.length > 0 && (
{asset.sellOrders && asset.sellOrders.length > 0 && (
<Column gap="6" flex="1" justifyContent="flex-end" whiteSpace="nowrap" style={{ maxWidth: '33%' }}>
<>
<Row className={subhead} color="textPrimary">
......
import { v4 as uuidv4 } from 'uuid'
import create from 'zustand'
import { devtools } from 'zustand/middleware'
......@@ -24,17 +23,21 @@ export const useSellAsset = create<SellAssetState>()(
sellAssets: [],
selectSellAsset: (asset) =>
set(({ sellAssets }) => {
const assetWithId = { id: uuidv4(), ...asset }
if (sellAssets.length === 0) return { sellAssets: [assetWithId] }
else return { sellAssets: [...sellAssets, assetWithId] }
if (sellAssets.length === 0) return { sellAssets: [asset] }
else return { sellAssets: [...sellAssets, asset] }
}),
removeSellAsset: (asset) => {
set(({ sellAssets }) => {
if (sellAssets.length === 0) return { sellAssets: [] }
else sellAssets.find((x) => x.id === asset.id)
else
sellAssets.find(
(x) => asset.tokenId === x.tokenId && x.asset_contract.address === asset.asset_contract.address
)
const assetsCopy = [...sellAssets]
assetsCopy.splice(
sellAssets.findIndex((n) => n.id === asset.id),
sellAssets.findIndex(
(n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address
),
1
)
return { sellAssets: assetsCopy }
......@@ -63,7 +66,9 @@ export const useSellAsset = create<SellAssetState>()(
if (listingIndex === 0) asset.marketAgnosticPrice = price
} else asset.newListings?.push({ price, marketplace, overrideFloorPrice: false })
} else asset.marketAgnosticPrice = price
const index = sellAssets.findIndex((n) => n.id === asset.id)
const index = sellAssets.findIndex(
(n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address
)
assetsCopy[index] = asset
return { sellAssets: assetsCopy }
})
......@@ -110,7 +115,9 @@ export const useSellAsset = create<SellAssetState>()(
set(({ sellAssets }) => {
const assetsCopy = [...sellAssets]
asset.listingWarnings?.push(warning)
const index = sellAssets.findIndex((n) => n.id === asset.id)
const index = sellAssets.findIndex(
(n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address
)
assetsCopy[index] = asset
return { sellAssets: assetsCopy }
})
......@@ -131,7 +138,9 @@ export const useSellAsset = create<SellAssetState>()(
asset.newListings[listingIndex].overrideFloorPrice = true
}
}
const index = sellAssets.findIndex((n) => n.id === asset.id)
const index = sellAssets.findIndex(
(n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address
)
assetsCopy[index] = asset
return { sellAssets: assetsCopy }
})
......
......@@ -9,7 +9,7 @@ export const createLooksRareOrder = async (payload: any): Promise<boolean> => {
})
try {
const data = await res.json()
return data.code === 200
return data.success
} catch (e) {
return false
}
......
......@@ -88,7 +88,7 @@ export interface GenieAsset {
name?: string
priceInfo: PriceInfo
susFlag?: boolean
sellorders?: Deprecated_SellOrder[] | SellOrder[] // TODO remove OldSellOrder when full migration to GraphQL is complete
sellorders?: Deprecated_SellOrder[] | SellOrder[] // TODO remove Deprecated_SellOrder when full migration to GraphQL is complete
smallImageUrl?: string
tokenId: string
tokenType: TokenType
......
......@@ -66,15 +66,9 @@ export interface WalletAsset {
asset_contract: {
address: string
schema_name: 'ERC1155' | 'ERC721' | string
asset_contract_type: string
created_date: string
name: string
symbol: string
description: string
external_link: string
image_url: string
default_to_fiat: boolean
only_proxied_transfers: boolean
payout_address: string
}
collection: GenieCollection
......@@ -84,7 +78,7 @@ export interface WalletAsset {
creatorPercentage: number
listing_date: string
date_acquired: string
sellOrders: Deprecated_SellOrder[]
sellOrders: Deprecated_SellOrder[] | SellOrder[] // TODO remove Deprecated_SellOrder when full migration to GraphQL is complete
floor_sell_order_price: number
// Used for creating new listings
expirationTime?: number
......
......@@ -6,6 +6,8 @@ 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 { ProfilePageLoadingSkeleton } from 'nft/components/profile/view/ProfilePageLoadingSkeleton'
import { lazy, Suspense, useEffect, useState } from 'react'
import { Navigate, Route, Routes, useLocation } from 'react-router-dom'
import { useIsDarkMode } from 'state/user/hooks'
......@@ -248,9 +250,30 @@ export default function App() {
</Suspense>
}
/>
<Route path="/nfts/profile" element={<Profile />} />
<Route path="/nfts/collection/:contractAddress" element={<Collection />} />
<Route path="/nfts/collection/:contractAddress/activity" element={<Collection />} />
<Route
path="/nfts/profile"
element={
<Suspense fallback={<ProfilePageLoadingSkeleton />}>
<Profile />
</Suspense>
}
/>
<Route
path="/nfts/collection/:contractAddress"
element={
<Suspense fallback={<CollectionPageSkeleton />}>
<Collection />
</Suspense>
}
/>
<Route
path="/nfts/collection/:contractAddress/activity"
element={
<Suspense fallback={<CollectionPageSkeleton />}>
<Collection />
</Suspense>
}
/>
</>
)}
</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