mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Publishers with most sales, using AccountTeaser component (#864)
* AccountTeaser used in Publishers with most sales, against /account_page * explorer link fixes * reduced stats values displayed, used Blockies component * sales number taken from subgraph * logs deleted, function renamed * sort publishers by sales from more networks * removed unused pagination * display & lint fixes * ranking added, useless css removed * unused query removed * types fix * pr fixes * loading fix * removed unused imports * css fixes, sales correct ordering * get publishers and all sales, css fix * err fix * get publishers and sales optimization * no duplicates fix Co-authored-by: ClaudiaHolhos <claudia@oceanprotocol.com>
This commit is contained in:
parent
28a2ba88c1
commit
34e424b13f
46
src/components/molecules/AccountTeaser.module.css
Normal file
46
src/components/molecules/AccountTeaser.module.css
Normal file
@ -0,0 +1,46 @@
|
||||
.blockies {
|
||||
aspect-ratio: 1/1;
|
||||
width: 13%;
|
||||
height: 13%;
|
||||
border-radius: 50%;
|
||||
margin-left: 0;
|
||||
margin-right: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.teaser {
|
||||
max-width: 40rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.link {
|
||||
composes: box from '../atoms/Box.module.css';
|
||||
padding: calc(var(--spacer) / 2) !important;
|
||||
font-size: var(--font-size-mini);
|
||||
height: 90%;
|
||||
color: var(--color-secondary);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.link span {
|
||||
font-size: var(--font-size-large);
|
||||
margin-right: calc(var(--spacer) / 3);
|
||||
}
|
||||
.name {
|
||||
margin-bottom: 0;
|
||||
font-size: var(--font-size-base) !important;
|
||||
padding-top: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sales {
|
||||
font-size: small;
|
||||
margin-top: -5px !important;
|
||||
margin-bottom: clac(var(--spacer) / 2);
|
||||
}
|
69
src/components/molecules/AccountTeaser.tsx
Normal file
69
src/components/molecules/AccountTeaser.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import { Link } from 'gatsby'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Dotdotdot from 'react-dotdotdot'
|
||||
import { Profile } from '../../models/Profile'
|
||||
import { accountTruncate } from '../../utils/web3'
|
||||
import get3BoxProfile from '../../utils/profile'
|
||||
import styles from './AccountTeaser.module.css'
|
||||
import Blockies from '../atoms/Blockies'
|
||||
import { useCancelToken } from '../../hooks/useCancelToken'
|
||||
import { getUserSales } from '../../utils/subgraph'
|
||||
import { useUserPreferences } from '../../providers/UserPreferences'
|
||||
|
||||
declare type AccountTeaserProps = {
|
||||
account: string
|
||||
place?: number
|
||||
}
|
||||
|
||||
const AccountTeaser: React.FC<AccountTeaserProps> = ({ account, place }) => {
|
||||
const [profile, setProfile] = useState<Profile>()
|
||||
const [sales, setSales] = useState(0)
|
||||
const newCancelToken = useCancelToken()
|
||||
const { chainIds } = useUserPreferences()
|
||||
|
||||
useEffect(() => {
|
||||
if (!account) return
|
||||
async function getProfileData() {
|
||||
const profile = await get3BoxProfile(account, newCancelToken())
|
||||
if (!profile) return
|
||||
setProfile(profile)
|
||||
}
|
||||
getProfileData()
|
||||
}, [account, newCancelToken])
|
||||
|
||||
useEffect(() => {
|
||||
if (!account) return
|
||||
async function getProfileSales() {
|
||||
const userSales = await getUserSales(account, chainIds)
|
||||
setSales(userSales)
|
||||
}
|
||||
getProfileSales()
|
||||
}, [account, chainIds])
|
||||
|
||||
return (
|
||||
<article className={styles.teaser}>
|
||||
<Link to={`/profile/${account}`} className={styles.link}>
|
||||
<header className={styles.header}>
|
||||
{place && <span>{place}</span>}
|
||||
{profile?.image ? (
|
||||
<img src={profile.image} className={styles.blockies} />
|
||||
) : (
|
||||
<Blockies accountId={account} className={styles.blockies} />
|
||||
)}
|
||||
<div>
|
||||
<Dotdotdot clamp={3}>
|
||||
<h3 className={styles.name}>
|
||||
{profile?.name ? profile?.name : accountTruncate(account)}
|
||||
</h3>
|
||||
</Dotdotdot>
|
||||
<p className={styles.sales}>
|
||||
{`${sales} ${sales === 1 ? 'sale' : 'sales'}`}
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
</Link>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountTeaser
|
55
src/components/organisms/AccountList.tsx
Normal file
55
src/components/organisms/AccountList.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import AccountTeaser from '../molecules/AccountTeaser'
|
||||
import React from 'react'
|
||||
import styles from './AssetList.module.css'
|
||||
import classNames from 'classnames/bind'
|
||||
import Loader from '../atoms/Loader'
|
||||
import { useUserPreferences } from '../../providers/UserPreferences'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
||||
function LoaderArea() {
|
||||
return (
|
||||
<div className={styles.loaderWrap}>
|
||||
<Loader />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
declare type AccountListProps = {
|
||||
accounts: string[]
|
||||
isLoading: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const AccountList: React.FC<AccountListProps> = ({
|
||||
accounts,
|
||||
isLoading,
|
||||
className
|
||||
}) => {
|
||||
const { chainIds } = useUserPreferences()
|
||||
|
||||
const styleClasses = cx({
|
||||
assetList: true,
|
||||
[className]: className
|
||||
})
|
||||
|
||||
return accounts && (isLoading === undefined || isLoading === false) ? (
|
||||
<>
|
||||
<div className={styleClasses}>
|
||||
{accounts.length > 0 ? (
|
||||
accounts.map((account, index) => (
|
||||
<AccountTeaser account={account} key={account} place={index + 1} />
|
||||
))
|
||||
) : chainIds.length === 0 ? (
|
||||
<div className={styles.empty}>No network selected.</div>
|
||||
) : (
|
||||
<div className={styles.empty}>No results found.</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<LoaderArea />
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountList
|
@ -8,10 +8,14 @@ import {
|
||||
queryMetadata
|
||||
} from '../../utils/aquarius'
|
||||
import Permission from '../organisms/Permission'
|
||||
import { getHighestLiquidityDatatokens } from '../../utils/subgraph'
|
||||
import {
|
||||
getTopAssetsPublishers,
|
||||
getHighestLiquidityDatatokens
|
||||
} from '../../utils/subgraph'
|
||||
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||
import { useUserPreferences } from '../../providers/UserPreferences'
|
||||
import styles from './Home.module.css'
|
||||
import AccountList from '../organisms/AccountList'
|
||||
import { useIsMounted } from '../../hooks/useIsMounted'
|
||||
import { useCancelToken } from '../../hooks/useCancelToken'
|
||||
import { SearchQuery } from '../../models/aquarius/SearchQuery'
|
||||
@ -45,6 +49,46 @@ function sortElements(items: DDO[], sorted: string[]) {
|
||||
return items
|
||||
}
|
||||
|
||||
function PublishersWithMostSales({
|
||||
title,
|
||||
action
|
||||
}: {
|
||||
title: ReactElement | string
|
||||
action?: ReactElement
|
||||
}) {
|
||||
const { chainIds } = useUserPreferences()
|
||||
const [result, setResult] = useState<string[]>([])
|
||||
const [loading, setLoading] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
async function init() {
|
||||
if (chainIds.length === 0) {
|
||||
const result: string[] = []
|
||||
setResult(result)
|
||||
setLoading(false)
|
||||
} else {
|
||||
try {
|
||||
setLoading(true)
|
||||
const publishers = await getTopAssetsPublishers(chainIds)
|
||||
setResult(publishers)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
Logger.error(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
init()
|
||||
}, [chainIds])
|
||||
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<h3>{title}</h3>
|
||||
<AccountList accounts={result} isLoading={loading} />
|
||||
{action && action}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function SectionQueryResult({
|
||||
title,
|
||||
query,
|
||||
@ -154,6 +198,7 @@ export default function HomePage(): ReactElement {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<PublishersWithMostSales title="Publishers with most sales" />
|
||||
</>
|
||||
</Permission>
|
||||
)
|
||||
|
@ -4,3 +4,9 @@
|
||||
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
||||
margin-top: var(--spacer);
|
||||
}
|
||||
|
||||
.accountTeaserStats {
|
||||
grid-template-columns: repeat(auto-fit, minmax(4rem, 1fr)) !important;
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
justify-items: center;
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ export default function Stats({
|
||||
<Conversion price={publisherLiquidity?.price} hideApproximateSymbol />
|
||||
}
|
||||
/>
|
||||
|
||||
<NumberUnit
|
||||
label="Total Liquidity"
|
||||
value={<Conversion price={`${totalLiquidity}`} hideApproximateSymbol />}
|
||||
|
@ -240,9 +240,9 @@ function ProfileProvider({
|
||||
cancelToken
|
||||
)
|
||||
setDownloads(downloads)
|
||||
setDownloadsTotal(downloads.length)
|
||||
setDownloadsTotal(downloads?.length)
|
||||
Logger.log(
|
||||
`[profile] Fetched ${downloads.length} download orders.`,
|
||||
`[profile] Fetched ${downloads?.length} download orders.`,
|
||||
downloads
|
||||
)
|
||||
},
|
||||
|
@ -2,7 +2,6 @@ import { gql, OperationResult, TypedDocumentNode, OperationContext } from 'urql'
|
||||
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||
import { getUrqlClientInstance } from '../providers/UrqlProvider'
|
||||
import { getOceanConfig } from './ocean'
|
||||
import web3 from 'web3'
|
||||
import {
|
||||
AssetsPoolPrice,
|
||||
AssetsPoolPrice_pools as AssetsPoolPricePool
|
||||
@ -26,6 +25,10 @@ import {
|
||||
} from '../@types/apollo/PoolShares'
|
||||
import { BestPrice } from '../models/BestPrice'
|
||||
import { OrdersData_tokenOrders as OrdersData } from '../@types/apollo/OrdersData'
|
||||
import {
|
||||
UserSalesQuery_users as UserSales,
|
||||
UserSalesQuery as UsersSalesList
|
||||
} from '../@types/apollo/UserSalesQuery'
|
||||
|
||||
export interface UserLiquidity {
|
||||
price: string
|
||||
@ -167,19 +170,6 @@ const HighestLiquidityAssets = gql`
|
||||
}
|
||||
`
|
||||
|
||||
const TotalAccountOrders = gql`
|
||||
query TotalAccountOrders($datatokenId_in: [String!]) {
|
||||
tokenOrders(where: { datatokenId_in: $datatokenId_in }) {
|
||||
payer {
|
||||
id
|
||||
}
|
||||
datatokenId {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const UserSharesQuery = gql`
|
||||
query UserSharesQuery($user: String, $pools: [String!]) {
|
||||
poolShares(where: { userAddress: $user, poolId_in: $pools }) {
|
||||
@ -252,6 +242,19 @@ const UserTokenOrders = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
const TopSalesQuery = gql`
|
||||
query TopSalesQuery {
|
||||
users(
|
||||
first: 20
|
||||
orderBy: nrSales
|
||||
orderDirection: desc
|
||||
where: { nrSales_not: 0 }
|
||||
) {
|
||||
id
|
||||
nrSales
|
||||
}
|
||||
}
|
||||
`
|
||||
const UserSalesQuery = gql`
|
||||
query UserSalesQuery($userSalesId: String) {
|
||||
users(where: { id: $userSalesId }) {
|
||||
@ -721,3 +724,52 @@ export async function getUserSales(
|
||||
Logger.log(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTopAssetsPublishers(
|
||||
chainIds: number[]
|
||||
): Promise<string[]> {
|
||||
const data: string[] = []
|
||||
const publisherSales: UserSales[] = []
|
||||
|
||||
for (const chain of chainIds) {
|
||||
const queryContext = getQueryContext(Number(chain))
|
||||
const fetchedUsers: OperationResult<UsersSalesList> = await fetchData(
|
||||
TopSalesQuery,
|
||||
null,
|
||||
queryContext
|
||||
)
|
||||
for (let i = 0; i < fetchedUsers.data.users.length; i++) {
|
||||
const publishersIndex = publisherSales.findIndex(
|
||||
(user) => fetchedUsers.data.users[i].id === user.id
|
||||
)
|
||||
if (publishersIndex === -1) {
|
||||
const publisher: UserSales = {
|
||||
id: fetchedUsers.data.users[i].id,
|
||||
nrSales: fetchedUsers.data.users[i].nrSales,
|
||||
__typename: 'User'
|
||||
}
|
||||
publisherSales.push(publisher)
|
||||
} else {
|
||||
const publisher: UserSales = {
|
||||
id: fetchedUsers.data.users[i].id,
|
||||
nrSales:
|
||||
fetchedUsers.data.users[i].nrSales +
|
||||
publisherSales[publishersIndex].nrSales,
|
||||
__typename: 'User'
|
||||
}
|
||||
publisherSales[publishersIndex] = publisher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publisherSales.sort((a, b) => b.nrSales - a.nrSales)
|
||||
for (let i = 0; i < publisherSales.length; i++) {
|
||||
if (data.length < 9) {
|
||||
data.push(publisherSales[i].id)
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user