Commit 68282af4 authored by Jack Short's avatar Jack Short Committed by GitHub

feat: activity port (#4702)

* adding activitySwither

* in the middle of porting activity

* ported over activity leaving - working on breakpoints

* updating responsive design

* updating responsive design

* addressed comments
parent 0ecb7323
import { style } from '@vanilla-extract/css'
import { body, buttonTextMedium, subhead, subheadSmall } from 'nft/css/common.css'
import { breakpoints, sprinkles, themeVars, vars } from 'nft/css/sprinkles.css'
export const baseRow = style([
sprinkles({
display: 'grid',
}),
{
gridTemplateColumns: '2.5fr 1fr',
'@media': {
[`screen and (min-width: ${breakpoints.sm}px)`]: {
gridTemplateColumns: '2fr 1.5fr 1fr',
},
[`screen and (min-width: ${breakpoints.md}px)`]: {
gridTemplateColumns: '1.75fr 1.4fr 1.1fr 1fr 1fr',
},
[`screen and (min-width: ${breakpoints.lg}px)`]: {
gridTemplateColumns: '1.75fr 1.4fr 1.1fr 1fr 1fr',
},
[`screen and (min-width: ${breakpoints.xl}px)`]: {
gridTemplateColumns: '1.75fr 1.4fr 1.1fr 1fr 1fr 1fr',
},
},
},
])
export const eventRow = style([
baseRow,
sprinkles({
paddingY: '12',
paddingX: '16',
color: 'textPrimary',
cursor: 'pointer',
borderWidth: '1px',
borderStyle: 'solid',
borderColor: 'transparent',
borderBottomColor: 'backgroundOutline',
}),
{
height: '84px',
':hover': {
background: themeVars.colors.backgroundSurface,
},
},
])
export const headerRow = style([
baseRow,
sprinkles({
paddingBottom: '8',
color: 'textSecondary',
fontSize: '12',
fontWeight: 'semibold',
paddingX: '16',
}),
{
lineHeight: '16px',
},
])
export const detailsImage = sprinkles({
width: '60',
height: '60',
borderRadius: '8',
})
export const detailsName = style([
body,
sprinkles({
marginBottom: '6',
fontWeight: 'normal',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}),
{
lineHeight: '24px',
},
])
export const eventDetail = style([
subhead,
sprinkles({
marginBottom: '4',
gap: '8',
}),
{
lineHeight: '24px',
},
])
export const eventTime = style([
subheadSmall,
sprinkles({
color: 'textSecondary',
}),
{
lineHeight: '20px',
},
])
export const addressCell = style([
subhead,
sprinkles({
color: 'textPrimary',
height: 'full',
justifyContent: 'center',
}),
{
lineHeight: '24px',
},
])
export const baseBuyCell = style([
buttonTextMedium,
sprinkles({
width: 'max',
background: 'none',
paddingY: '12',
paddingX: '16',
border: 'none',
borderRadius: '12',
transition: '250',
}),
{
lineHeight: '20px',
},
])
export const buyCell = style([
baseBuyCell,
{
selectors: {
'&:enabled': {
cursor: 'pointer',
color: vars.color.blue400,
},
'&:disabled': {
color: themeVars.colors.textTertiary,
},
'&:hover&:enabled': {
background: vars.color.blue400,
color: themeVars.colors.explicitWhite,
},
},
},
])
export const removeCell = style([
baseBuyCell,
sprinkles({
color: 'accentFailure',
cursor: 'pointer',
}),
{
':hover': {
background: vars.color.accentFailure,
color: themeVars.colors.explicitWhite,
},
},
])
export const filter = style([
subheadSmall,
sprinkles({
background: 'backgroundInteractive',
color: 'textPrimary',
paddingY: '12',
paddingX: '16',
borderRadius: '12',
cursor: 'pointer',
}),
{
boxSizing: 'border-box',
},
])
export const activeFilter = style([
filter,
sprinkles({
borderWidth: '1px',
borderStyle: 'solid',
borderColor: 'genieBlue',
}),
])
export const marketplaceIcon = style([
sprinkles({
width: '16',
height: '16',
borderRadius: '4',
flexShrink: '0',
}),
])
export const ensNameContainer = sprinkles({
width: 'max',
})
export const rarityInfo = style([
sprinkles({
display: 'flex',
borderRadius: '4',
height: '16',
width: 'min',
color: 'textPrimary',
background: 'backgroundInteractive',
fontSize: '10',
fontWeight: 'semibold',
paddingX: '4',
cursor: 'pointer',
}),
{
lineHeight: '12px',
letterSpacing: '0.04em',
backdropFilter: 'blur(6px)',
},
])
import clsx from 'clsx'
import { Box } from 'nft/components/Box'
import { LoadingSparkle } from 'nft/components/common/Loading/LoadingSparkle'
import { Center, Column, Row } from 'nft/components/Flex'
import { useBag, useIsMobile } from 'nft/hooks'
import { ActivityFetcher } from 'nft/queries/genie/ActivityFetcher'
import { ActivityEvent, ActivityEventResponse, ActivityEventType } from 'nft/types'
import { fetchPrice } from 'nft/utils/fetchPrice'
import { useCallback, useEffect, useMemo, useReducer, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component'
import { useInfiniteQuery } from 'react-query'
import * as styles from './Activity.css'
import { AddressCell, BuyCell, EventCell, ItemCell, PriceCell } from './ActivityCells'
import { ActivityLoader } from './ActivityLoader'
enum ColumnHeaders {
Item = 'Item',
Event = 'Event',
Price = 'Price',
By = 'By',
To = 'To',
}
export const HeaderRow = () => {
return (
<Box className={styles.headerRow}>
<Box>{ColumnHeaders.Item}</Box>
<Box>{ColumnHeaders.Event}</Box>
<Box display={{ sm: 'none', md: 'block' }}>{ColumnHeaders.Price}</Box>
<Box display={{ sm: 'none', xl: 'block' }}>{ColumnHeaders.By}</Box>
<Box display={{ sm: 'none', xxl: 'block' }}>{ColumnHeaders.To}</Box>
<Box display={{ sm: 'none', lg: 'block' }}>Buy</Box>
</Box>
)
}
interface ActivityProps {
contractAddress: string
rarityVerified: boolean
collectionName: string
}
const initialFilterState = {
[ActivityEventType.Listing]: true,
[ActivityEventType.Sale]: true,
[ActivityEventType.Transfer]: false,
[ActivityEventType.CancelListing]: false,
}
const reduceFilters = (state: typeof initialFilterState, action: { eventType: ActivityEventType }) => {
return { ...state, [action.eventType]: !state[action.eventType] }
}
const baseHref = (event: ActivityEvent) => `/#/nfts/asset/${event.collectionAddress}/${event.tokenId}?origin=activity`
export const Activity = ({ contractAddress, rarityVerified, collectionName }: ActivityProps) => {
const [activeFilters, filtersDispatch] = useReducer(reduceFilters, initialFilterState)
const {
data: eventsData,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isSuccess,
isLoading,
} = useInfiniteQuery<ActivityEventResponse>(
[
'collectionActivity',
{
contractAddress,
activeFilters,
},
],
async ({ pageParam = '' }) => {
return await ActivityFetcher(
contractAddress,
{
eventTypes: Object.keys(activeFilters)
.filter((key) => activeFilters[key as ActivityEventType])
.map((key) => key as ActivityEventType),
},
pageParam
)
},
{
getNextPageParam: (lastPage) => {
return lastPage.events?.length === 25 ? lastPage.cursor : undefined
},
refetchInterval: 15000,
refetchIntervalInBackground: false,
refetchOnWindowFocus: false,
refetchOnMount: false,
}
)
const events = useMemo(
() => (isSuccess ? eventsData?.pages.map((page) => page.events).flat() : null),
[isSuccess, eventsData]
)
const itemsInBag = useBag((state) => state.itemsInBag)
const addAssetToBag = useBag((state) => state.addAssetToBag)
const removeAssetFromBag = useBag((state) => state.removeAssetFromBag)
const cartExpanded = useBag((state) => state.bagExpanded)
const toggleCart = useBag((state) => state.toggleBag)
const isMobile = useIsMobile()
const [ethPriceInUSD, setEthPriceInUSD] = useState(0)
useEffect(() => {
fetchPrice().then((price) => {
setEthPriceInUSD(price || 0)
})
}, [])
const Filter = useCallback(
function ActivityFilter({ eventType }: { eventType: ActivityEventType }) {
const isActive = activeFilters[eventType]
return (
<Box
className={clsx(styles.filter, isActive && styles.activeFilter)}
onClick={() => filtersDispatch({ eventType })}
>
{eventType.charAt(0) + eventType.slice(1).toLowerCase() + 's'}
</Box>
)
},
[activeFilters]
)
return (
<Box>
<Row gap="8">
<Filter eventType={ActivityEventType.Listing} />
<Filter eventType={ActivityEventType.Sale} />
<Filter eventType={ActivityEventType.Transfer} />
</Row>
{isLoading && <ActivityLoader />}
{events && (
<Column marginTop="36">
<HeaderRow />
<InfiniteScroll
next={fetchNextPage}
hasMore={!!hasNextPage}
loader={
isFetchingNextPage ? (
<Center paddingY="20">
<LoadingSparkle />
</Center>
) : null
}
dataLength={events?.length ?? 0}
style={{ overflow: 'unset' }}
>
{events.map((event, i) => (
<Box as="a" href={baseHref(event)} className={styles.eventRow} key={i}>
<ItemCell event={event} rarityVerified={rarityVerified} collectionName={collectionName} />
<EventCell
eventType={event.eventType}
eventTimestamp={event.eventTimestamp}
eventTransactionHash={event.transactionHash}
/>
<PriceCell marketplace={event.marketplace} price={event.price} />
<AddressCell address={event.fromAddress} />
<AddressCell address={event.toAddress} desktopLBreakpoint />
<BuyCell
event={event}
collectionName={collectionName}
selectAsset={addAssetToBag}
removeAsset={removeAssetFromBag}
itemsInBag={itemsInBag}
cartExpanded={cartExpanded}
toggleCart={toggleCart}
isMobile={isMobile}
ethPriceInUSD={ethPriceInUSD}
/>
</Box>
))}
</InfiniteScroll>
</Column>
)}
</Box>
)
}
import { MouseoverTooltip } from 'components/Tooltip'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import {
ActivityExternalLinkIcon,
ActivityListingIcon,
ActivitySaleIcon,
ActivityTransferIcon,
RarityVerifiedIcon,
} from 'nft/components/icons'
import {
ActivityEvent,
ActivityEventType,
ActivityEventTypeDisplay,
BagItem,
GenieAsset,
Markets,
OrderStatus,
TokenMetadata,
TokenRarity,
} from 'nft/types'
import { shortenAddress } from 'nft/utils/address'
import { buildActivityAsset } from 'nft/utils/buildActivityAsset'
import { formatEthPrice } from 'nft/utils/currency'
import { getTimeDifference, isValidDate } from 'nft/utils/date'
import { putCommas } from 'nft/utils/putCommas'
import { fallbackProvider, getRarityProviderLogo } from 'nft/utils/rarity'
import { MouseEvent, useMemo, useState } from 'react'
import * as styles from './Activity.css'
const formatListingStatus = (status: OrderStatus): string => {
switch (status) {
case OrderStatus.EXECUTED:
return 'Sold'
case OrderStatus.CANCELLED:
return 'Cancelled'
case OrderStatus.EXPIRED:
return 'Expired'
case OrderStatus.VALID:
return 'Buy Now'
}
}
interface BuyCellProps {
event: ActivityEvent
collectionName: string
selectAsset: (asset: GenieAsset) => void
removeAsset: (asset: GenieAsset) => void
itemsInBag: BagItem[]
cartExpanded: boolean
toggleCart: () => void
isMobile: boolean
ethPriceInUSD: number
}
export const BuyCell = ({
event,
collectionName,
selectAsset,
removeAsset,
itemsInBag,
cartExpanded,
toggleCart,
isMobile,
ethPriceInUSD,
}: BuyCellProps) => {
const asset = useMemo(
() => buildActivityAsset(event, collectionName, ethPriceInUSD),
[event, collectionName, ethPriceInUSD]
)
const isSelected = useMemo(() => {
return itemsInBag.some((item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address)
}, [asset, itemsInBag])
return (
<Column display={{ sm: 'none', lg: 'flex' }} height="full" justifyContent="center" marginX="auto">
{event.eventType === ActivityEventType.Listing && event.orderStatus ? (
<Box
as="button"
className={event.orderStatus === OrderStatus.VALID && isSelected ? styles.removeCell : styles.buyCell}
onClick={(e: MouseEvent) => {
e.preventDefault()
isSelected ? removeAsset(asset) : selectAsset(asset)
!isSelected && !cartExpanded && !isMobile && toggleCart()
}}
disabled={event.orderStatus !== OrderStatus.VALID}
>
{event.orderStatus === OrderStatus.VALID ? (
<>{`${itemsInBag.length === 0 ? 'Buy Now' : isSelected ? 'Remove' : 'Add to bag'}`}</>
) : (
<>{`${formatListingStatus(event.orderStatus)}`}</>
)}
</Box>
) : (
'-'
)}
</Column>
)
}
interface AddressCellProps {
address?: string
desktopLBreakpoint?: boolean
}
export const AddressCell = ({ address, desktopLBreakpoint }: AddressCellProps) => {
return (
<Column
display={{ sm: 'none', xl: desktopLBreakpoint ? 'none' : 'flex', xxl: 'flex' }}
className={styles.addressCell}
>
<Box>{address ? shortenAddress(address, 2, 4) : '-'}</Box>
</Column>
)
}
const MarketplaceIcon = ({ marketplace }: { marketplace: Markets }) => {
return (
<Box
as="img"
alt={marketplace}
src={`/nft/svgs/marketplaces/${marketplace}.svg`}
className={styles.marketplaceIcon}
/>
)
}
const PriceTooltip = ({ price }: { price: string }) => (
<MouseoverTooltip
text={
<Box textAlign="left" fontSize="14" fontWeight="normal" color="textSecondary">
{`${price} ETH`}
</Box>
}
placement="top"
>
<Box>{`${price.substring(0, 5)}... ETH`}</Box>
</MouseoverTooltip>
)
export const PriceCell = ({ marketplace, price }: { marketplace?: Markets; price?: string }) => {
const formattedPrice = useMemo(() => (price ? putCommas(formatEthPrice(price)).toString() : null), [price])
return (
<Row display={{ sm: 'none', md: 'flex' }} gap="8">
{marketplace && <MarketplaceIcon marketplace={marketplace} />}
{formattedPrice ? (
formattedPrice.length > 6 ? (
<PriceTooltip price={formattedPrice} />
) : (
<>{`${formattedPrice} ETH`}</>
)
) : (
<>-</>
)}
</Row>
)
}
interface EventCellProps {
eventType: ActivityEventType
eventTimestamp?: number
eventTransactionHash?: string
}
const renderEventIcon = (eventType: ActivityEventType) => {
switch (eventType) {
case ActivityEventType.Listing:
return <ActivityListingIcon width={16} height={16} />
case ActivityEventType.Sale:
return <ActivitySaleIcon width={16} height={16} />
case ActivityEventType.Transfer:
return <ActivityTransferIcon width={16} height={16} />
default:
return null
}
}
const openEtherscanLinkInNewTab = (e: MouseEvent, transactionHash: string) => {
e.preventDefault()
window.open(`https://etherscan.io/tx/${transactionHash}`, '_blank', 'noopener,noreferrer')
}
const ExternalLinkIcon = ({ transactionHash }: { transactionHash: string }) => (
<Row onClick={(e: MouseEvent) => openEtherscanLinkInNewTab(e, transactionHash)} marginLeft="4">
<ActivityExternalLinkIcon />
</Row>
)
const eventColors = (eventType: ActivityEventType) => {
const activityEvents = {
[ActivityEventType.Listing]: 'gold',
[ActivityEventType.Sale]: 'green',
[ActivityEventType.Transfer]: 'violet',
[ActivityEventType.CancelListing]: 'error',
}
return activityEvents[eventType] as 'gold' | 'green' | 'violet' | 'accentFailure'
}
export const EventCell = ({ eventType, eventTimestamp, eventTransactionHash }: EventCellProps) => {
return (
<Column height="full" justifyContent="center" gap="4">
<Row className={styles.eventDetail} color={eventColors(eventType)}>
{renderEventIcon(eventType)}
{ActivityEventTypeDisplay[eventType]}
</Row>
{eventTimestamp && isValidDate(eventTimestamp) && (
<Row className={styles.eventTime}>
{getTimeDifference(eventTimestamp.toString())}
{eventTransactionHash && <ExternalLinkIcon transactionHash={eventTransactionHash} />}
</Row>
)}
</Column>
)
}
interface ItemCellProps {
event: ActivityEvent
rarityVerified: boolean
collectionName: string
}
const NoContentContainer = () => (
<Box
position="relative"
style={{
background: `#24272e`,
}}
className={styles.detailsImage}
>
<Box
position="absolute"
textAlign="center"
left="1/2"
top="1/2"
style={{ transform: 'translate3d(-50%, -50%, 0)' }}
color="grey500"
fontSize="12"
fontWeight="normal"
>
Image
<br />
not
<br />
available
</Box>
</Box>
)
interface RankingProps {
rarity: TokenRarity
collectionName: string
rarityVerified: boolean
}
const Ranking = ({ rarity, collectionName, rarityVerified }: RankingProps) => {
const rarityProviderLogo = getRarityProviderLogo(rarity.source)
return (
<MouseoverTooltip
text={
<Row>
<Box display="flex" marginRight="4">
<img src={rarityProviderLogo} alt="cardLogo" width={16} />
</Box>
<Box width="full" fontSize="14">
{rarityVerified
? `Verified by ${collectionName}`
: `Ranking by ${rarity.source === 'Genie' ? fallbackProvider : rarity.source}`}
</Box>
</Row>
}
placement="top"
>
<Box className={styles.rarityInfo}>
<Box paddingTop="2" paddingBottom="2" display="flex">
{putCommas(rarity.rank)}
</Box>
<Box display="flex" height="16">
{rarityVerified ? <RarityVerifiedIcon /> : null}
</Box>
</Box>
</MouseoverTooltip>
)
}
const getItemImage = (tokenMetadata?: TokenMetadata): string | undefined => {
return tokenMetadata?.smallImageUrl || tokenMetadata?.imageUrl
}
export const ItemCell = ({ event, rarityVerified, collectionName }: ItemCellProps) => {
const [loaded, setLoaded] = useState(false)
const [noContent, setNoContent] = useState(!getItemImage(event.tokenMetadata))
return (
<Row gap="16" overflow="hidden" whiteSpace="nowrap">
{!noContent ? (
<Box
as={'img'}
alt={event.tokenMetadata?.name || event.tokenId}
src={getItemImage(event.tokenMetadata)}
draggable={false}
className={styles.detailsImage}
style={{
background: loaded ? 'none' : '#24272e',
}}
onLoad={() => setLoaded(true)}
onError={() => setNoContent(true)}
/>
) : (
<NoContentContainer />
)}
<Column height="full" justifyContent="center" overflow="hidden" whiteSpace="nowrap" marginRight="24">
<Box className={styles.detailsName}>{event.tokenMetadata?.name || event.tokenId}</Box>
{event.tokenMetadata?.rarity && (
<Ranking
rarity={event.tokenMetadata?.rarity}
rarityVerified={rarityVerified}
collectionName={collectionName}
/>
)}
</Column>
</Row>
)
}
import { style } from '@vanilla-extract/css'
import { loadingBlock } from 'nft/css/loading.css'
import { sprinkles } from 'nft/css/sprinkles.css'
export const loadingSquare = style([
loadingBlock,
sprinkles({
width: '60',
height: '60',
borderRadius: '8',
}),
])
export const loadingSliver = style([
loadingBlock,
sprinkles({
height: '16',
borderRadius: 'round',
}),
{
width: '108px',
},
])
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { HeaderRow } from './Activity'
import { eventRow } from './Activity.css'
import * as styles from './ActivityLoader.css'
const LoadingSquare = () => {
return <Box className={styles.loadingSquare} />
}
const LoadingSliver = () => {
return <Box className={styles.loadingSliver} />
}
export const ActivityLoader = () => {
return (
<Column marginTop="36">
<HeaderRow />
{[...Array(10)].map((_, i) => (
<Box as="a" className={eventRow} key={i}>
<Row gap="16">
<LoadingSquare />
<LoadingSliver />
</Row>
<Row>
<LoadingSliver />
</Row>
<Row display={{ sm: 'none', md: 'flex' }}>
<LoadingSliver />
</Row>
<Row display={{ sm: 'none', lg: 'flex' }}>
<LoadingSliver />
</Row>
<Row display={{ sm: 'none', xl: 'flex' }}>
<LoadingSliver />
</Row>
</Box>
))}
</Column>
)
}
import { style } from '@vanilla-extract/css'
import { buttonTextMedium } from 'nft/css/common.css'
import { sprinkles, vars } from 'nft/css/sprinkles.css'
export const baseActivitySwitcherToggle = style([
buttonTextMedium,
sprinkles({
position: 'relative',
background: 'none',
border: 'none',
cursor: 'pointer',
}),
{
lineHeight: '24px',
},
])
export const activitySwitcherToggle = style([
baseActivitySwitcherToggle,
sprinkles({
color: 'textSecondary',
}),
])
export const selectedActivitySwitcherToggle = style([
baseActivitySwitcherToggle,
sprinkles({
color: 'textPrimary',
}),
{
':after': {
content: '',
position: 'absolute',
background: vars.color.genieBlue,
width: '100%',
height: '2px',
left: '0px',
right: '0px',
bottom: '-8px',
},
},
])
import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import * as styles from './ActivitySwitcher.css'
export const ActivitySwitcher = ({
showActivity,
toggleActivity,
}: {
showActivity: boolean
toggleActivity: () => void
}) => {
return (
<Row gap="24" marginBottom="28">
<Box
as="button"
className={showActivity ? styles.activitySwitcherToggle : styles.selectedActivitySwitcherToggle}
onClick={() => showActivity && toggleActivity()}
>
Items
</Box>
<Box
as="button"
className={!showActivity ? styles.activitySwitcherToggle : styles.selectedActivitySwitcherToggle}
onClick={() => !showActivity && toggleActivity()}
>
Activity
</Box>
</Row>
)
}
export { Activity } from './Activity'
export { ActivitySwitcher } from './ActivitySwitcher'
export { CollectionNfts } from './CollectionNfts'
export { CollectionSearch } from './CollectionSearch'
export { CollectionStats } from './CollectionStats'
......
......@@ -7,6 +7,9 @@ const themeContractValues = {
accentActionSoft: '',
explicitWhite: '',
gold: '',
green: '',
violet: '',
backgroundAction: '',
backgroundFloating: '',
......@@ -158,8 +161,12 @@ export const vars = createGlobalTheme(':root', {
red700: '#530f10',
red400: '#FA2C38',
red300: '#FD766B',
gold200: '#EEB317',
gold400: '#B17900',
green200: '#5CFE9D',
green400: '#1A9550',
violet200: '#BDB8FA',
violet400: '#7A7BEB',
grey900: '#0E111A',
grey800: '#141B2B',
grey700: '#293249',
......
import { AnimatedBox, Box } from 'nft/components/Box'
import { CollectionNfts, CollectionStats, Filters } from 'nft/components/collection'
import { Activity, ActivitySwitcher, CollectionNfts, CollectionStats, Filters } from 'nft/components/collection'
import { Column, Row } from 'nft/components/Flex'
import { useBag, useCollectionFilters, useFiltersExpanded, useIsMobile } from 'nft/hooks'
import * as styles from 'nft/pages/collection/index.css'
import { CollectionStatsFetcher } from 'nft/queries'
import { useEffect } from 'react'
import { useQuery } from 'react-query'
import { useParams } from 'react-router-dom'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { useSpring } from 'react-spring/web'
import * as styles from './index.css'
const FILTER_WIDTH = 332
const BAG_WIDTH = 324
......@@ -16,7 +17,10 @@ const Collection = () => {
const { contractAddress } = useParams()
const isMobile = useIsMobile()
const [isFiltersExpanded] = useFiltersExpanded()
const [isFiltersExpanded, setFiltersExpanded] = useFiltersExpanded()
const { pathname } = useLocation()
const navigate = useNavigate()
const isActivityToggled = pathname.includes('/activity')
const setMarketCount = useCollectionFilters((state) => state.setMarketCount)
const isBagExpanded = useBag((state) => state.bagExpanded)
......@@ -43,6 +47,12 @@ const Collection = () => {
setMarketCount(marketCount)
}, [collectionStats?.marketplaceCount, setMarketCount])
const toggleActivity = () => {
isActivityToggled
? navigate(`/nfts/collection/${contractAddress}`)
: navigate(`/nfts/collection/${contractAddress}/activity`)
}
return (
<Column width="full">
{collectionStats && contractAddress ? (
......@@ -57,11 +67,16 @@ const Collection = () => {
className={`${styles.bannerImage}`}
/>
</Box>
{collectionStats && (
<Row paddingLeft="32" paddingRight="32">
<CollectionStats stats={collectionStats} isMobile={isMobile} />
</Row>
)}
<Column paddingX="32">
{collectionStats && <CollectionStats stats={collectionStats} isMobile={isMobile} />}
<ActivitySwitcher
showActivity={isActivityToggled}
toggleActivity={() => {
isFiltersExpanded && setFiltersExpanded(false)
toggleActivity()
}}
/>
</Column>
<Row alignItems="flex-start" position="relative" paddingX="48">
<Box position="sticky" top="72" width="0">
{isFiltersExpanded && (
......@@ -79,11 +94,21 @@ const Collection = () => {
width: gridWidthOffset.interpolate((x) => `calc(100% - ${x as number}px)`),
}}
>
<CollectionNfts
collectionStats={collectionStats}
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified}
/>
{isActivityToggled
? contractAddress && (
<Activity
contractAddress={contractAddress}
rarityVerified={collectionStats?.rarityVerified ?? false}
collectionName={collectionStats?.name ?? ''}
/>
)
: contractAddress && (
<CollectionNfts
contractAddress={contractAddress}
collectionStats={collectionStats}
rarityVerified={collectionStats?.rarityVerified}
/>
)}
</AnimatedBox>
</Row>
</>
......
......@@ -6,6 +6,9 @@ export const darkTheme: Theme = {
accentActionSoft: '#000000E5',
explicitWhite: '#FFFFFF',
green: vars.color.green200,
gold: vars.color.gold200,
violet: vars.color.violet200,
backgroundAction: vars.color.blue400,
backgroundFloating: '0000000C',
......
......@@ -17,6 +17,9 @@ export const lightTheme: Theme = {
modalBackdrop: 'rgba(0, 0, 0, 0.3)',
stateOverlayHover: `rgba(153,161,189,0.08)`,
green: vars.color.green400,
gold: vars.color.gold400,
violet: vars.color.violet400,
textPrimary: vars.color.grey900,
textSecondary: vars.color.grey500,
......
......@@ -11,7 +11,7 @@ export const rarityProviderLogo: { [key: string]: string } = {
Genie: fallbackProviderLogo,
}
export const getRarityProviderLogo = (source?: string): string | null => {
if (!source) return null
export const getRarityProviderLogo = (source?: string): string | undefined => {
if (!source) return undefined
return rarityProviderLogo[source] || fallbackProviderLogo
}
......@@ -232,10 +232,11 @@ export default function App() {
{nftFlag === NftVariant.Enabled && (
<>
<Route path="/nfts/collection/:contractAddress" element={<Collection />} />
<Route path="/nfts" element={<NftExplore />} />
<Route path="/nfts/sell" element={<Sell />} />
<Route path="/nfts/asset/:contractAddress/:tokenId" element={<Asset />} />
<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