Skip to content

Commit

Permalink
CRT marketplace improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
WRadoslaw committed Apr 16, 2024
1 parent 21df7a5 commit 55d47d2
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import BN from 'bn.js'
import { useMemo } from 'react'

import { useBasicChannels } from '@/api/hooks/channel'
import { ChannelOrderByInput } from '@/api/queries/__generated__/baseTypes.generated'
import { BasicChannelFieldsFragment } from '@/api/queries/__generated__/fragments.generated'
import { SvgActionCreatorToken, SvgActionVerified } from '@/assets/icons'
import { SvgEmptyStateIllustration } from '@/assets/illustrations'
import { JoyTokenIcon } from '@/components/JoyTokenIcon'
import { NumberFormat } from '@/components/NumberFormat'
import { Section } from '@/components/Section/Section'
import { TableProps } from '@/components/Table'
import { RightAlignedHeader } from '@/components/Table/Table.styles'
import { Text } from '@/components/Text'
import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader'
import { absoluteRoutes } from '@/config/routes'
import { useMediaMatch } from '@/hooks/useMediaMatch'

import { Avatar } from '../Avatar'
import {
JoyAmountWrapper,
SenderItemIconsWrapper,
SkeletonChannelContainer,
StyledLink,
StyledListItem,
StyledTable,
} from '../TopSellingChannelsTable/TopSellingChannelsTable.styles'

const COLUMNS: TableProps['columns'] = [
{
Header: '',
accessor: 'index',
width: 1,
},
{
Header: 'CHANNEL',
accessor: 'channel',
width: 9,
},
{
Header: () => <RightAlignedHeader>SALES VOLUME</RightAlignedHeader>,
accessor: 'salesVolume',
width: 4,
},
]

const tableEmptyState = {
title: 'No channels found',
description: 'No top earning channels with minted token have been found.',
icon: <SvgEmptyStateIllustration />,
}

export const TopEarningChannels = ({ withCrtOnly }: { withCrtOnly?: boolean }) => {
const { channels, loading } = useBasicChannels({
limit: 10,
orderBy: [ChannelOrderByInput.CumulativeRevenueDesc],
where: {
cumulativeRevenue_gt: '0',
...(withCrtOnly ? { creatorToken_isNull: false } : {}),
},
})

const lgMatch = useMediaMatch('lg')
const mappedData: TableProps['data'] = useMemo(() => {
return loading
? Array.from({ length: 10 }, () => ({
index: null,
channel: (
<SkeletonChannelContainer>
<SkeletonLoader width={32} height={32} rounded />
<SkeletonLoader width="30%" height={20} />
</SkeletonChannelContainer>
),
salesVolume: <SkeletonLoader width="100%" height={16} />,
}))
: channels?.map((data, index) => ({
index: (
<Text variant="t100" as="p" color="colorTextMuted">
{index + 1}
</Text>
),
salesVolume: (
<JoyAmountWrapper>
<NumberFormat
icon={<JoyTokenIcon variant="gray" />}
variant="t200-strong"
as="p"
value={new BN(data.cumulativeRevenue)}
margin={{ left: 1 }}
format="short"
withDenomination
denominationAlign="right"
/>
</JoyAmountWrapper>
),
channel: <Channel channel={data} />,
})) ?? []
}, [channels, loading])

if (!loading && !channels) {
return null
}

return (
<Section
headerProps={{
start: {
type: 'title',
title: `Top earning channels ${withCrtOnly ? 'with a token' : ''}`,
},
}}
contentProps={{
type: 'grid',
grid: {
sm: {
columns: 1,
},
},
children: [
<StyledTable
key="single"
minWidth={400}
emptyState={tableEmptyState}
columns={COLUMNS}
data={mappedData}
doubleColumn={lgMatch}
/>,
],
}}
/>
)
}

const Channel = ({ channel }: { channel: BasicChannelFieldsFragment }) => {
const hasCreatorToken = !!channel.creatorToken?.token.id
// todo to be implemented
const verified = false
return (
<StyledLink to={absoluteRoutes.viewer.channel(channel.id)} title={channel.title || ''}>
<StyledListItem
nodeStart={<Avatar assetUrls={channel.avatarPhoto?.resolvedUrls ?? undefined} />}
label={channel.title}
isInteractive={false}
nodeEnd={
<SenderItemIconsWrapper>
{hasCreatorToken && (
<span title="Creator token">
<SvgActionCreatorToken />
</span>
)}
{verified && (
<span title="Verified">
<SvgActionVerified />
</span>
)}
</SenderItemIconsWrapper>
}
/>
</StyledLink>
)
}
1 change: 1 addition & 0 deletions packages/atlas/src/components/TopEarningChannels/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './TopEarningChannels'
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const AllTokensSection = () => {
const tableData =
tokens?.map(({ createdAt, accountsNum, lastPrice, totalSupply, status, symbol, channel }) => ({
createdAt: new Date(createdAt),
totalRevenue: 0,
totalRevenue: new BN(channel?.channel.cumulativeRevenue ?? 0),
holdersNum: accountsNum,
isVerified: false,
marketCap: lastPrice && totalSupply ? hapiBnToTokenNumber(new BN(lastPrice).mul(new BN(totalSupply))) ?? 0 : 0,
Expand Down
22 changes: 19 additions & 3 deletions packages/atlas/src/components/_crt/CrtCard/CrtCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { css } from '@emotion/react'
import styled from '@emotion/styled'
import { ReactElement, useMemo } from 'react'
import { Link } from 'react-router-dom'

import { SvgActionAuction, SvgActionMarket, SvgActionNotForSale, SvgActionNotifications } from '@/assets/icons'
import { Avatar } from '@/components/Avatar'
Expand All @@ -11,6 +12,7 @@ import { Button } from '@/components/_buttons/Button'
import { CrtMainInfo, CrtMainInfoProps } from '@/components/_crt/CrtBasicInfoWidget'
import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader'
import { DetailsContent, DetailsContentProps } from '@/components/_nft/NftTile'
import { absoluteRoutes } from '@/config/routes'
import { cVar, sizes } from '@/styles'

type SaleProps = {
Expand All @@ -27,6 +29,7 @@ export type CrtSaleTypes = MarketProps | SaleProps | { type: 'inactive' }

export type CrtCardProps = {
marketCap?: number
channelId: string
channelRevenue?: number
size?: 'medium' | 'small'
status?: CrtSaleTypes
Expand All @@ -36,6 +39,7 @@ export type CrtCardProps = {

export const CrtCard = ({
channelRevenue,
channelId,
marketCap,
size,
status,
Expand Down Expand Up @@ -165,7 +169,7 @@ export const CrtCard = ({
}

return (
<FlexBox className={className} flow="column" gap={0}>
<Container to={absoluteRoutes.viewer.channel(channelId, { tab: 'Token' })} className={className}>
<CrtMainInfo size={size} {...mainInfoProps}>
<AvatarBox width="100%" justifyContent="space-between">
<Avatar
Expand All @@ -176,15 +180,27 @@ export const CrtCard = ({
<Button icon={<SvgActionNotifications />} variant="secondary" rounded size={isSmall ? 'medium' : 'small'} />
</AvatarBox>
</CrtMainInfo>
<DetailsWrapper size={size}>
<DetailsWrapper className="crt-card-details" size={size}>
{details.map((detail, idx) => (
<StyledDetailsContent key={idx} {...detail} isInactive={status?.type === 'inactive'} tileSize={size} />
))}
</DetailsWrapper>
</FlexBox>
</Container>
)
}

const Container = styled(Link)`
display: flex;
flex-direction: column;
text-decoration: none;
:hover {
opacity: 0.85;
}
cursor: pointer;
`

const StyledDetailsContent = styled(DetailsContent)<{ isInactive?: boolean }>`
display: flex;
flex-direction: column;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import styled from '@emotion/styled'
import BN from 'bn.js'
import { useMemo } from 'react'

import { TokenStatus } from '@/api/queries/__generated__/baseTypes.generated'
Expand Down Expand Up @@ -44,7 +45,7 @@ export type MarketplaceToken = {
status: TokenStatus
createdAt: Date
marketCap: number
totalRevenue: number
totalRevenue: BN
holdersNum: number
channelId: string
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useGetBasicCreatorTokensQuery } from '@/api/queries/__generated__/creat
import { LimitedWidthContainer } from '@/components/LimitedWidthContainer'
import { MarketplaceCarousel } from '@/components/NftCarousel/MarketplaceCarousel'
import { Section } from '@/components/Section/Section'
import { TopSellingChannelsTable } from '@/components/TopSellingChannelsTable'
import { TopEarningChannels } from '@/components/TopEarningChannels'
import { AllTokensSection } from '@/components/_crt/AllTokensSection'
import { CrtCard, CrtSaleTypes } from '@/components/_crt/CrtCard/CrtCard'
import { useMediaMatch } from '@/hooks/useMediaMatch'
Expand All @@ -29,7 +29,7 @@ export const MarketplaceCrtTab = () => {
const filteredTokens = data?.creatorTokens.filter((token) => !!token.trailerVideo.length).slice(0, 10) ?? []

const featuredCrts =
filteredTokens.map(
data?.creatorTokens.map(
({ id, symbol, channel, totalSupply, accountsNum, lastPrice, currentAmmSale, description, currentSale }) => {
const status: CrtSaleTypes = currentSale
? {
Expand All @@ -49,6 +49,7 @@ export const MarketplaceCrtTab = () => {
<StyledCrtCard
key={id}
status={status}
channelId={channel?.channel.id ?? ''}
symbol={symbol ?? 'N/A'}
avatar={channel?.channel.avatarPhoto?.resolvedUrls[0]}
marketCap={
Expand Down Expand Up @@ -86,7 +87,7 @@ export const MarketplaceCrtTab = () => {
)}
<TableFullWitdhtWrapper>
<LimitedWidthContainer big noBottomPadding>
<TopSellingChannelsTable withCrtOnly />
<TopEarningChannels withCrtOnly />
</LimitedWidthContainer>
</TableFullWitdhtWrapper>
<AllTokensSection />
Expand Down

0 comments on commit 55d47d2

Please sign in to comment.