From 42956a344127fc18e388ab8f7329b6be06d39af2 Mon Sep 17 00:00:00 2001 From: EnzoVezzaro Date: Tue, 5 Jul 2022 14:49:35 +0200 Subject: [PATCH] restored "Publishers with most sales" section (#1540) * updated subgraph query * rework AccountList * fix missing 'a' elem in dom ( is not rendering correct markup) * restore PublishersWithMostSales section * removed unnecessary params in AssetList * use Blockies for all profile.image * changed logic on getTopAssetsPublishers * added aggregations op to getTopAssetsPublishers method * fix build issues * improve logic & added correct total sales on profile * removed complex logic to unify query * typography & markup cleanup Co-authored-by: Matthias Kretschmann --- src/@types/aquarius/BaseQueryParams.d.ts | 1 + src/@types/aquarius/PagedAssets.d.ts | 1 + src/@types/aquarius/SearchQuery.ts | 4 +- src/@utils/aquarius.ts | 72 ++++++++++++++++++- src/@utils/subgraph.ts | 66 +++++------------ .../@shared/AccountList/AccountList.tsx | 4 +- .../@shared/AccountList/index.module.css | 24 +++++++ .../AccountTeaser/AccountTeaser.module.css | 53 ++++++-------- .../@shared/AccountTeaser/AccountTeaser.tsx | 50 ++++++------- .../@shared/atoms/Blockies/index.tsx | 6 +- .../Home/PublishersWithMostSales.tsx | 8 ++- src/components/Home/index.tsx | 6 +- 12 files changed, 177 insertions(+), 118 deletions(-) create mode 100644 src/components/@shared/AccountList/index.module.css diff --git a/src/@types/aquarius/BaseQueryParams.d.ts b/src/@types/aquarius/BaseQueryParams.d.ts index f987e3e5c..7517480f0 100644 --- a/src/@types/aquarius/BaseQueryParams.d.ts +++ b/src/@types/aquarius/BaseQueryParams.d.ts @@ -9,6 +9,7 @@ interface BaseQueryParams { nestedQuery?: any esPaginationOptions?: EsPaginationOptions sortOptions?: SortOptions + aggs?: any filters?: FilterTerm[] ignorePurgatory?: boolean } diff --git a/src/@types/aquarius/PagedAssets.d.ts b/src/@types/aquarius/PagedAssets.d.ts index 2e30d7070..a3535a3c0 100644 --- a/src/@types/aquarius/PagedAssets.d.ts +++ b/src/@types/aquarius/PagedAssets.d.ts @@ -3,4 +3,5 @@ interface PagedAssets { page: number totalPages: number totalResults: number + aggregations: any } diff --git a/src/@types/aquarius/SearchQuery.ts b/src/@types/aquarius/SearchQuery.ts index e8b062ee7..29caa7f97 100644 --- a/src/@types/aquarius/SearchQuery.ts +++ b/src/@types/aquarius/SearchQuery.ts @@ -5,7 +5,8 @@ export enum SortDirectionOptions { export enum SortTermOptions { Created = 'metadata.created', - Relevance = '_score' + Relevance = '_score', + Stats = 'stats.orders' } // Note: could not figure out how to get `enum` to be ambiant @@ -43,5 +44,6 @@ declare global { // eslint-disable-next-line @typescript-eslint/no-explicit-any query: any sort?: { [jsonPath: string]: SortDirectionOptions } + aggs?: any } } diff --git a/src/@utils/aquarius.ts b/src/@utils/aquarius.ts index 8eb6208c9..2d831773c 100644 --- a/src/@utils/aquarius.ts +++ b/src/@utils/aquarius.ts @@ -55,6 +55,10 @@ export function generateBaseQuery( } } as SearchQuery + if (baseQueryParams.aggs !== undefined) { + generatedQuery.aggs = baseQueryParams.aggs + } + if (baseQueryParams.sortOptions !== undefined) generatedQuery.sort = { [baseQueryParams.sortOptions.sortBy]: @@ -74,12 +78,15 @@ export function transformQueryResult( results: [], page: 0, totalPages: 0, - totalResults: 0 + totalResults: 0, + aggregations: [] } result.results = (queryResult.hits.hits || []).map( (hit) => hit._source as Asset ) + + result.aggregations = queryResult.aggregations result.totalResults = queryResult.hits.total.value result.totalPages = result.totalResults / size < 1 @@ -307,6 +314,13 @@ export async function getPublishedAssets( sortBy: SortTermOptions.Created, sortDirection: SortDirectionOptions.Descending }, + aggs: { + totalOrders: { + sum: { + field: SortTermOptions.Stats + } + } + }, esPaginationOptions: { from: (Number(page) - 1 || 0) * 9, size: 9 @@ -314,6 +328,62 @@ export async function getPublishedAssets( } as BaseQueryParams const query = generateBaseQuery(baseQueryParams) + + try { + const result = await queryMetadata(query, cancelToken) + return result + } catch (error) { + if (axios.isCancel(error)) { + LoggerInstance.log(error.message) + } else { + LoggerInstance.error(error.message) + } + } +} + +export async function getTopPublishers( + chainIds: number[], + cancelToken: CancelToken, + page?: number, + type?: string, + accesType?: string +): Promise { + const filters: FilterTerm[] = [] + + accesType !== undefined && + filters.push(getFilterTerm('services.type', accesType)) + type !== undefined && filters.push(getFilterTerm('metadata.type', type)) + + const baseQueryParams = { + chainIds, + filters, + sortOptions: { + sortBy: SortTermOptions.Created, + sortDirection: SortDirectionOptions.Descending + }, + aggs: { + topPublishers: { + terms: { + field: 'nft.owner.keyword', + order: { totalSales: 'desc' } + }, + aggs: { + totalSales: { + sum: { + field: SortTermOptions.Stats + } + } + } + } + }, + esPaginationOptions: { + from: (Number(page) - 1 || 0) * 9, + size: 9 + } + } as BaseQueryParams + + const query = generateBaseQuery(baseQueryParams) + try { const result = await queryMetadata(query, cancelToken) return result diff --git a/src/@utils/subgraph.ts b/src/@utils/subgraph.ts index 7b32da86b..78df23fca 100644 --- a/src/@utils/subgraph.ts +++ b/src/@utils/subgraph.ts @@ -18,7 +18,7 @@ import { OpcFeesQuery as OpcFeesData } from '../@types/subgraph/OpcFeesQuery' import { calcSingleOutGivenPoolIn } from './pool' import Decimal from 'decimal.js' import { MAX_DECIMALS } from './constants' - +import { getPublishedAssets, getTopPublishers } from '@utils/aquarius' export interface UserLiquidity { price: string oceanBalance: string @@ -172,19 +172,11 @@ const UserSalesQuery = gql` } ` -// TODO: figure out some way to get this const TopSalesQuery = gql` query TopSalesQuery { - users( - first: 20 - orderBy: sharesOwned - orderDirection: desc - where: { tokenBalancesOwned_not: "0" } - ) { + users(first: 20, orderBy: totalSales, orderDirection: desc) { id - tokenBalancesOwned { - value - } + totalSales } } ` @@ -419,20 +411,10 @@ export async function getUserSales( accountId: string, chainIds: number[] ): Promise { - const variables = { user: accountId?.toLowerCase() } try { - const userSales = await fetchDataForMultipleChains( - UserSalesQuery, - variables, - chainIds - ) - let salesSum = 0 - for (let i = 0; i < userSales.length; i++) { - if (userSales[i].users.length > 0) { - salesSum += parseInt(userSales[i].users[0].totalSales) - } - } - return salesSum + const result = await getPublishedAssets(accountId, chainIds, null) + const { totalOrders } = result.aggregations + return totalOrders.value } catch (error) { LoggerInstance.error('Error getUserSales', error.message) } @@ -442,33 +424,19 @@ export async function getTopAssetsPublishers( chainIds: number[], nrItems = 9 ): Promise { - const publisherSales: AccountTeaserVM[] = [] + const publishers: AccountTeaserVM[] = [] - for (const chain of chainIds) { - const queryContext = getQueryContext(Number(chain)) - const fetchedUsers: OperationResult = 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.address - ) - if (publishersIndex === -1) { - const publisher: AccountTeaserVM = { - address: fetchedUsers.data.users[i].id, - nrSales: fetchedUsers.data.users[i].totalSales - } - publisherSales.push(publisher) - } else { - publisherSales[publishersIndex].nrSales += - publisherSales[publishersIndex].nrSales - } - } + const result = await getTopPublishers(chainIds, null) + const { topPublishers } = result.aggregations + + for (let i = 0; i < topPublishers.buckets.length; i++) { + publishers.push({ + address: topPublishers.buckets[i].key, + nrSales: parseInt(topPublishers.buckets[i].totalSales.value) + }) } - publisherSales.sort((a, b) => b.nrSales - a.nrSales) + publishers.sort((a, b) => b.nrSales - a.nrSales) - return publisherSales.slice(0, nrItems) + return publishers.slice(0, nrItems) } diff --git a/src/components/@shared/AccountList/AccountList.tsx b/src/components/@shared/AccountList/AccountList.tsx index bef2e323f..fc2a9fa6d 100644 --- a/src/components/@shared/AccountList/AccountList.tsx +++ b/src/components/@shared/AccountList/AccountList.tsx @@ -1,5 +1,5 @@ import React, { ReactElement } from 'react' -import styles from './AssetList.module.css' +import styles from './index.module.css' import classNames from 'classnames/bind' import Loader from '../atoms/Loader' import { useUserPreferences } from '@context/UserPreferences' @@ -29,7 +29,7 @@ export default function AccountList({ const { chainIds } = useUserPreferences() const styleClasses = cx({ - assetList: true, + accountList: true, [className]: className }) diff --git a/src/components/@shared/AccountList/index.module.css b/src/components/@shared/AccountList/index.module.css new file mode 100644 index 000000000..735058718 --- /dev/null +++ b/src/components/@shared/AccountList/index.module.css @@ -0,0 +1,24 @@ +.accountList { + display: grid; + grid-template-columns: 1fr; + gap: calc(var(--spacer) / 2); +} + +@media screen and (min-width: 25rem) { + .accountList { + grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); + gap: var(--spacer); + } +} + +.empty { + color: var(--color-secondary); + font-size: var(--font-size-small); + font-style: italic; +} + +.loaderWrap { + display: flex; + justify-content: center; + align-items: center; +} diff --git a/src/components/@shared/AccountTeaser/AccountTeaser.module.css b/src/components/@shared/AccountTeaser/AccountTeaser.module.css index 47af5f397..e042e0f7f 100644 --- a/src/components/@shared/AccountTeaser/AccountTeaser.module.css +++ b/src/components/@shared/AccountTeaser/AccountTeaser.module.css @@ -1,46 +1,39 @@ .blockies { aspect-ratio: 1/1; - width: 13%; - height: 13%; + width: calc(var(--font-size-large) * 2) !important; + height: calc(var(--font-size-large) * 2) !important; border-radius: 50%; margin-left: 0; - margin-right: calc(var(--spacer) / 4); + margin-right: calc(var(--spacer) / 3); } .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%; + padding: calc(var(--spacer) / 3) calc(var(--spacer) / 2); 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); +.place { + font-size: var(--font-size-large); + margin-right: calc(var(--spacer) / 2); +} + +.name { + margin-bottom: 0; + font-size: var(--font-size-base); + padding-top: calc(var(--spacer) / 5); + color: var(--font-color-text); +} + +.sales { + margin: 0; +} + +.sales span { + font-weight: var(--font-weight-bold); + color: var(--font-color-text); } diff --git a/src/components/@shared/AccountTeaser/AccountTeaser.tsx b/src/components/@shared/AccountTeaser/AccountTeaser.tsx index 7de6685c6..4a20867a7 100644 --- a/src/components/@shared/AccountTeaser/AccountTeaser.tsx +++ b/src/components/@shared/AccountTeaser/AccountTeaser.tsx @@ -33,34 +33,26 @@ export default function AccountTeaser({ }, [accountTeaserVM, newCancelToken]) return ( -
- -
- {place && {place}} - {profile?.image ? ( - - ) : ( - - )} -
- -

- {profile?.name - ? profile?.name - : accountTruncate(accountTeaserVM.address)} -

-
-

- {`${accountTeaserVM.nrSales} ${ - accountTeaserVM.nrSales === 1 ? 'sale' : 'sales' - }`} -

-
-
- -
+ + + {place && {place}} + +
+ + {profile?.name + ? profile?.name + : accountTruncate(accountTeaserVM.address)} + +

+ {accountTeaserVM.nrSales} + {`${accountTeaserVM.nrSales === 1 ? ' sale' : ' sales'}`} +

+
+
+ ) } diff --git a/src/components/@shared/atoms/Blockies/index.tsx b/src/components/@shared/atoms/Blockies/index.tsx index 9957ddefe..ba3a23070 100644 --- a/src/components/@shared/atoms/Blockies/index.tsx +++ b/src/components/@shared/atoms/Blockies/index.tsx @@ -5,11 +5,13 @@ import styles from './index.module.css' export interface BlockiesProps { accountId: string className?: string + image?: string } export default function Blockies({ accountId, - className + className, + image }: BlockiesProps): ReactElement { if (!accountId) return null @@ -18,7 +20,7 @@ export default function Blockies({ return ( diff --git a/src/components/Home/PublishersWithMostSales.tsx b/src/components/Home/PublishersWithMostSales.tsx index 94b495fd4..c36b7857b 100644 --- a/src/components/Home/PublishersWithMostSales.tsx +++ b/src/components/Home/PublishersWithMostSales.tsx @@ -1,8 +1,9 @@ import { useUserPreferences } from '@context/UserPreferences' +import { LoggerInstance } from '@oceanprotocol/lib' import AccountList from '@shared/AccountList/AccountList' import { getTopAssetsPublishers } from '@utils/subgraph' import React, { ReactElement, useEffect, useState } from 'react' -import styles from './Home.module.css' +import styles from './index.module.css' export default function PublishersWithMostSales({ title, @@ -17,18 +18,19 @@ export default function PublishersWithMostSales({ useEffect(() => { async function init() { + setLoading(true) if (chainIds.length === 0) { const result: AccountTeaserVM[] = [] setResult(result) setLoading(false) } else { try { - setLoading(true) const publishers = await getTopAssetsPublishers(chainIds) setResult(publishers) setLoading(false) } catch (error) { - // Logger.error(error.message) + LoggerInstance.error(error.message) + setLoading(false) } } } diff --git a/src/components/Home/index.tsx b/src/components/Home/index.tsx index 68d670d42..cc0da2e30 100644 --- a/src/components/Home/index.tsx +++ b/src/components/Home/index.tsx @@ -14,6 +14,7 @@ import styles from './index.module.css' import { useIsMounted } from '@hooks/useIsMounted' import { useCancelToken } from '@hooks/useCancelToken' import { SortTermOptions } from '../../@types/aquarius/SearchQuery' +import PublishersWithMostSales from './PublishersWithMostSales' async function getQueryHighest( chainIds: number[] @@ -66,7 +67,8 @@ function SectionQueryResult({ results: [], page: 0, totalPages: 0, - totalResults: 0 + totalResults: 0, + aggregations: undefined } setResult(result) setLoading(false) @@ -153,6 +155,8 @@ export default function HomePage(): ReactElement { } /> + + ) }