mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Add top allocations (#1722)
* add top * copy, section move * asset teaser prototyping * more moving elements around * copy * allocated on asset details stats area * ve networks "hacks" * fix type * added hacks to display top allocation * fixed chainIds * remove comment * add ve in asset stats * remove console * added formating and totals * update total allocated * update allocation * fixed numbers * fix build * added teaser warn when no price * number formatting Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
52eaee0ad5
commit
b38f30a67a
14
package-lock.json
generated
14
package-lock.json
generated
@ -13,7 +13,7 @@
|
||||
"@coingecko/cryptoformat": "^0.5.4",
|
||||
"@loadable/component": "^5.15.2",
|
||||
"@oceanprotocol/art": "^3.2.0",
|
||||
"@oceanprotocol/lib": "^2.1.1",
|
||||
"@oceanprotocol/lib": "^2.2.1",
|
||||
"@oceanprotocol/typographies": "^0.1.0",
|
||||
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
@ -4539,9 +4539,9 @@
|
||||
"integrity": "sha512-Oe+oBRiu1dlco9PQ7eUYcTYi2Nua69S3TiSw62H46AIpwnFK8ORuO0Ny20No++KisBA9F+84b5lDn6kQy5Lt/Q=="
|
||||
},
|
||||
"node_modules/@oceanprotocol/lib": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.1.1.tgz",
|
||||
"integrity": "sha512-N7NKnwVujJDn2X9MwxFu15x8VvTVEDqWuIZFY4s3NG0NbwXGEHptewKlAVhOkvm6jOGuCN3NXWqRUTvMFOWGbQ==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.2.1.tgz",
|
||||
"integrity": "sha512-HNmT3DJJeyvFRwCbmgJucGpte90epIhgSy+68PSc83TLKRW2CF4N1mioMkoGxMwnK3rJzj6tEy4R9NKKLbdT5w==",
|
||||
"dependencies": {
|
||||
"@oceanprotocol/contracts": "^1.1.7",
|
||||
"bignumber.js": "^9.1.0",
|
||||
@ -44803,9 +44803,9 @@
|
||||
"integrity": "sha512-Oe+oBRiu1dlco9PQ7eUYcTYi2Nua69S3TiSw62H46AIpwnFK8ORuO0Ny20No++KisBA9F+84b5lDn6kQy5Lt/Q=="
|
||||
},
|
||||
"@oceanprotocol/lib": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.1.1.tgz",
|
||||
"integrity": "sha512-N7NKnwVujJDn2X9MwxFu15x8VvTVEDqWuIZFY4s3NG0NbwXGEHptewKlAVhOkvm6jOGuCN3NXWqRUTvMFOWGbQ==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-2.2.1.tgz",
|
||||
"integrity": "sha512-HNmT3DJJeyvFRwCbmgJucGpte90epIhgSy+68PSc83TLKRW2CF4N1mioMkoGxMwnK3rJzj6tEy4R9NKKLbdT5w==",
|
||||
"requires": {
|
||||
"@oceanprotocol/contracts": "^1.1.7",
|
||||
"bignumber.js": "^9.1.0",
|
||||
|
@ -26,7 +26,7 @@
|
||||
"@coingecko/cryptoformat": "^0.5.4",
|
||||
"@loadable/component": "^5.15.2",
|
||||
"@oceanprotocol/art": "^3.2.0",
|
||||
"@oceanprotocol/lib": "^2.1.1",
|
||||
"@oceanprotocol/lib": "^2.2.1",
|
||||
"@oceanprotocol/typographies": "^0.1.0",
|
||||
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
|
@ -9,7 +9,6 @@ export interface AppConfig {
|
||||
metadataCacheUri: string
|
||||
infuraProjectId: string
|
||||
chainIds: number[]
|
||||
chainIdsSupported: number[]
|
||||
marketFeeAddress: string
|
||||
publisherMarketOrderFee: string
|
||||
publisherMarketFixedSwapFee: string
|
||||
|
@ -360,12 +360,12 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
||||
// -----------------------------------
|
||||
|
||||
useEffect(() => {
|
||||
if (appConfig.chainIdsSupported.includes(networkId)) {
|
||||
if (appConfig.chainIds.includes(networkId)) {
|
||||
setIsSupportedOceanNetwork(true)
|
||||
} else {
|
||||
setIsSupportedOceanNetwork(false)
|
||||
}
|
||||
}, [networkId, appConfig.chainIdsSupported])
|
||||
}, [networkId, appConfig.chainIds])
|
||||
|
||||
// -----------------------------------
|
||||
// Handle change events
|
||||
|
@ -6,7 +6,8 @@ export enum SortDirectionOptions {
|
||||
export enum SortTermOptions {
|
||||
Created = 'nft.created',
|
||||
Relevance = '_score',
|
||||
Stats = 'stats.orders'
|
||||
Stats = 'stats.orders',
|
||||
Allocated = 'stats.allocated'
|
||||
}
|
||||
|
||||
// Note: could not figure out how to get `enum` to be ambiant
|
||||
|
@ -53,7 +53,9 @@ export function generateBaseQuery(
|
||||
...baseQueryParams.nestedQuery,
|
||||
filter: [
|
||||
...(baseQueryParams.filters || []),
|
||||
getFilterTerm('chainId', baseQueryParams.chainIds),
|
||||
baseQueryParams.chainIds
|
||||
? getFilterTerm('chainId', baseQueryParams.chainIds)
|
||||
: [],
|
||||
getFilterTerm('_index', 'aquarius'),
|
||||
...(baseQueryParams.ignorePurgatory
|
||||
? []
|
||||
|
45
src/@utils/veAllocation.ts
Normal file
45
src/@utils/veAllocation.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { AllLocked } from 'src/@types/subgraph/AllLocked'
|
||||
import { gql, OperationResult } from 'urql'
|
||||
import { fetchData, getQueryContext } from './subgraph'
|
||||
import axios from 'axios'
|
||||
|
||||
const AllLocked = gql`
|
||||
query AllLocked {
|
||||
veOCEANs(first: 1000) {
|
||||
lockedAmount
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
interface TotalVe {
|
||||
totalLocked: number
|
||||
totalAllocated: number
|
||||
}
|
||||
|
||||
export async function getTotalAllocatedAndLocked(): Promise<TotalVe> {
|
||||
const totals = {
|
||||
totalLocked: 0,
|
||||
totalAllocated: 0
|
||||
}
|
||||
|
||||
const queryContext = getQueryContext(1)
|
||||
|
||||
const response = await axios.post(`https://df-sql.oceandao.org/nftinfo`)
|
||||
totals.totalAllocated = response.data?.reduce(
|
||||
(previousValue: number, currentValue: { ve_allocated: any }) =>
|
||||
previousValue + Number(currentValue.ve_allocated),
|
||||
0
|
||||
)
|
||||
|
||||
const fetchedLocked: OperationResult<AllLocked, any> = await fetchData(
|
||||
AllLocked,
|
||||
null,
|
||||
queryContext
|
||||
)
|
||||
totals.totalLocked = fetchedLocked.data?.veOCEANs.reduce(
|
||||
(previousValue, currentValue) =>
|
||||
previousValue + Number(currentValue.lockedAmount),
|
||||
0
|
||||
)
|
||||
return totals
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import AssetTeaser from '@shared/AssetTeaser/AssetTeaser'
|
||||
import AssetTeaser from '@shared/AssetTeaser'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import Pagination from '@shared/Pagination'
|
||||
import styles from './index.module.css'
|
||||
|
@ -1,62 +0,0 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import Link from 'next/link'
|
||||
import Dotdotdot from 'react-dotdotdot'
|
||||
import Price from '@shared/Price'
|
||||
import removeMarkdown from 'remove-markdown'
|
||||
import Publisher from '@shared/Publisher'
|
||||
import AssetType from '@shared/AssetType'
|
||||
import NetworkName from '@shared/NetworkName'
|
||||
import styles from './AssetTeaser.module.css'
|
||||
import { getServiceByName } from '@utils/ddo'
|
||||
|
||||
declare type AssetTeaserProps = {
|
||||
asset: AssetExtended
|
||||
noPublisher?: boolean
|
||||
}
|
||||
|
||||
export default function AssetTeaser({
|
||||
asset,
|
||||
noPublisher
|
||||
}: AssetTeaserProps): ReactElement {
|
||||
const { name, type, description } = asset.metadata
|
||||
const { datatokens } = asset
|
||||
const isCompute = Boolean(getServiceByName(asset, 'compute'))
|
||||
const accessType = isCompute ? 'compute' : 'access'
|
||||
const { owner } = asset.nft
|
||||
const { orders } = asset.stats
|
||||
return (
|
||||
<article className={`${styles.teaser} ${styles[type]}`}>
|
||||
<Link href={`/asset/${asset.id}`}>
|
||||
<a className={styles.link}>
|
||||
<header className={styles.header}>
|
||||
<div className={styles.symbol}>{datatokens[0]?.symbol}</div>
|
||||
<Dotdotdot tagName="h1" clamp={3} className={styles.title}>
|
||||
{name.slice(0, 200)}
|
||||
</Dotdotdot>
|
||||
{!noPublisher && (
|
||||
<Publisher account={owner} minimal className={styles.publisher} />
|
||||
)}
|
||||
</header>
|
||||
|
||||
<AssetType
|
||||
type={type}
|
||||
accessType={accessType}
|
||||
className={styles.typeDetails}
|
||||
totalSales={orders}
|
||||
/>
|
||||
|
||||
<div className={styles.content}>
|
||||
<Dotdotdot tagName="p" clamp={3}>
|
||||
{removeMarkdown(description?.substring(0, 300) || '')}
|
||||
</Dotdotdot>
|
||||
</div>
|
||||
|
||||
<footer className={styles.foot}>
|
||||
<Price accessDetails={asset.accessDetails} size="small" />
|
||||
<NetworkName networkId={asset.chainId} className={styles.network} />
|
||||
</footer>
|
||||
</a>
|
||||
</Link>
|
||||
</article>
|
||||
)
|
||||
}
|
@ -9,6 +9,8 @@
|
||||
height: 100%;
|
||||
color: var(--color-secondary);
|
||||
position: relative;
|
||||
padding-top: calc(var(--spacer) / 2);
|
||||
padding-bottom: calc(var(--spacer) / 2);
|
||||
/* for sticking footer to bottom */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -18,8 +20,12 @@
|
||||
background-color: var(--background-body);
|
||||
}
|
||||
|
||||
.detailLine {
|
||||
margin-bottom: calc(var(--spacer) / 2);
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
margin-top: calc(var(--spacer) / 3);
|
||||
overflow-wrap: break-word;
|
||||
hyphens: auto;
|
||||
/* for sticking footer to bottom */
|
||||
@ -27,7 +33,7 @@
|
||||
}
|
||||
|
||||
.content p {
|
||||
margin-bottom: calc(var(--spacer) / 4);
|
||||
margin-bottom: calc(var(--spacer) / 3);
|
||||
}
|
||||
|
||||
.title {
|
||||
@ -37,36 +43,20 @@
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.publisher {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.foot {
|
||||
.footer {
|
||||
margin-top: calc(var(--spacer) / 4);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.foot p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.symbol {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.typeDetails {
|
||||
position: absolute;
|
||||
top: calc(var(--spacer) / 3);
|
||||
right: calc(var(--spacer) / 3);
|
||||
width: auto;
|
||||
.typeLabel {
|
||||
font-size: var(--font-size-mini);
|
||||
display: inline-block;
|
||||
border-left: 1px solid var(--border-color);
|
||||
padding-left: calc(var(--spacer) / 3.5);
|
||||
margin-left: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.network {
|
||||
font-size: var(--font-size-mini);
|
||||
position: absolute;
|
||||
right: calc(var(--spacer) / 3);
|
||||
bottom: calc(var(--spacer) / 3);
|
||||
.typeLabel:first-child {
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
}
|
84
src/components/@shared/AssetTeaser/index.tsx
Normal file
84
src/components/@shared/AssetTeaser/index.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import Link from 'next/link'
|
||||
import Dotdotdot from 'react-dotdotdot'
|
||||
import Price from '@shared/Price'
|
||||
import removeMarkdown from 'remove-markdown'
|
||||
import Publisher from '@shared/Publisher'
|
||||
import AssetType from '@shared/AssetType'
|
||||
import NetworkName from '@shared/NetworkName'
|
||||
import styles from './index.module.css'
|
||||
import { getServiceByName } from '@utils/ddo'
|
||||
import { formatPrice } from '@shared/Price/PriceUnit'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
|
||||
declare type AssetTeaserProps = {
|
||||
asset: AssetExtended
|
||||
noPublisher?: boolean
|
||||
}
|
||||
|
||||
export default function AssetTeaser({
|
||||
asset,
|
||||
noPublisher
|
||||
}: AssetTeaserProps): ReactElement {
|
||||
const { name, type, description } = asset.metadata
|
||||
const { datatokens } = asset
|
||||
const isCompute = Boolean(getServiceByName(asset, 'compute'))
|
||||
const accessType = isCompute ? 'compute' : 'access'
|
||||
const { owner } = asset.nft
|
||||
const { orders, allocated } = asset.stats
|
||||
const isUnsupportedPricing = asset?.accessDetails?.type === 'NOT_SUPPORTED'
|
||||
const { locale } = useUserPreferences()
|
||||
|
||||
return (
|
||||
<article className={`${styles.teaser} ${styles[type]}`}>
|
||||
<Link href={`/asset/${asset.id}`}>
|
||||
<a className={styles.link}>
|
||||
<aside className={styles.detailLine}>
|
||||
<AssetType
|
||||
className={styles.typeLabel}
|
||||
type={type}
|
||||
accessType={accessType}
|
||||
/>
|
||||
<span className={styles.typeLabel}>{datatokens[0]?.symbol}</span>
|
||||
<NetworkName
|
||||
networkId={asset.chainId}
|
||||
className={styles.typeLabel}
|
||||
/>
|
||||
</aside>
|
||||
<header className={styles.header}>
|
||||
<Dotdotdot tagName="h1" clamp={3} className={styles.title}>
|
||||
{name.slice(0, 200)}
|
||||
</Dotdotdot>
|
||||
{!noPublisher && <Publisher account={owner} minimal />}
|
||||
</header>
|
||||
<div className={styles.content}>
|
||||
<Dotdotdot tagName="p" clamp={3}>
|
||||
{removeMarkdown(description?.substring(0, 300) || '')}
|
||||
</Dotdotdot>
|
||||
</div>
|
||||
{isUnsupportedPricing ? (
|
||||
<strong>No pricing schema available</strong>
|
||||
) : (
|
||||
<Price accessDetails={asset.accessDetails} size="small" />
|
||||
)}
|
||||
<footer className={styles.footer}>
|
||||
{allocated && allocated > 0 ? (
|
||||
<span className={styles.typeLabel}>
|
||||
{allocated < 0
|
||||
? ''
|
||||
: `${formatPrice(allocated, locale)} veOCEAN`}
|
||||
</span>
|
||||
) : null}
|
||||
{orders && orders > 0 ? (
|
||||
<span className={styles.typeLabel}>
|
||||
{orders < 0
|
||||
? 'N/A'
|
||||
: `${orders} ${orders === 1 ? 'sale' : 'sales'}`}
|
||||
</span>
|
||||
) : null}
|
||||
</footer>
|
||||
</a>
|
||||
</Link>
|
||||
</article>
|
||||
)
|
||||
}
|
@ -7,13 +7,11 @@ import Lock from '@images/lock.svg'
|
||||
export default function AssetType({
|
||||
type,
|
||||
accessType,
|
||||
className,
|
||||
totalSales
|
||||
className
|
||||
}: {
|
||||
type: string
|
||||
accessType: string
|
||||
className?: string
|
||||
totalSales?: number
|
||||
}): ReactElement {
|
||||
return (
|
||||
<div className={className || null}>
|
||||
@ -28,14 +26,6 @@ export default function AssetType({
|
||||
<div className={styles.typeLabel}>
|
||||
{type === 'dataset' ? 'dataset' : 'algorithm'}
|
||||
</div>
|
||||
|
||||
{(totalSales || totalSales === 0) && (
|
||||
<div className={styles.typeLabel}>
|
||||
{totalSales < 0
|
||||
? 'N/A'
|
||||
: `${totalSales} ${totalSales === 1 ? 'sale' : 'sales'}`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const DatasetSchema = (): object => {
|
||||
|
||||
const networksMain = filterNetworksByType(
|
||||
'mainnet',
|
||||
appConfig.chainIdsSupported,
|
||||
appConfig.chainIds,
|
||||
networksList
|
||||
)
|
||||
|
||||
|
@ -20,6 +20,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.stat {
|
||||
border-left: 1px solid var(--border-color);
|
||||
padding-left: calc(var(--spacer) / 3.5);
|
||||
margin-left: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.stat:first-child {
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--font-color-heading);
|
||||
|
@ -1,21 +1,33 @@
|
||||
import { useAsset } from '@context/Asset'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import { formatPrice } from '@shared/Price/PriceUnit'
|
||||
import React from 'react'
|
||||
import styles from './index.module.css'
|
||||
|
||||
export default function AssetStats() {
|
||||
const { locale } = useUserPreferences()
|
||||
const { asset } = useAsset()
|
||||
const { orders, allocated } = asset.stats
|
||||
|
||||
return (
|
||||
<footer className={styles.stats}>
|
||||
{!asset || !asset?.stats || asset?.stats?.orders < 0 ? (
|
||||
{allocated && allocated > 0 ? (
|
||||
<span className={styles.stat}>
|
||||
<span className={styles.number}>
|
||||
{formatPrice(allocated, locale)}
|
||||
</span>
|
||||
veOCEAN
|
||||
</span>
|
||||
) : null}
|
||||
{!asset || !asset?.stats || orders < 0 ? (
|
||||
'N/A'
|
||||
) : asset?.stats?.orders === 0 ? (
|
||||
) : orders === 0 ? (
|
||||
'No sales yet'
|
||||
) : (
|
||||
<>
|
||||
<span className={styles.number}>{asset.stats.orders}</span> sale
|
||||
{asset.stats.orders === 1 ? '' : 's'}
|
||||
</>
|
||||
<span className={styles.stat}>
|
||||
<span className={styles.number}>{orders}</span> sale
|
||||
{orders === 1 ? '' : 's'}
|
||||
</span>
|
||||
)}
|
||||
</footer>
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import PriceUnit from '@shared/Price/PriceUnit'
|
||||
import React, { ReactElement } from 'react'
|
||||
import { StatsTotal } from './_types'
|
||||
|
||||
@ -8,9 +9,12 @@ export default function MarketStatsTotal({
|
||||
}): ReactElement {
|
||||
return (
|
||||
<>
|
||||
<strong>{total.orders}</strong> orders across{' '}
|
||||
<strong>{total.nfts}</strong> assets with{' '}
|
||||
<strong>{total.datatokens}</strong> different datatokens.
|
||||
<PriceUnit price={total.orders} size="small" /> orders across{' '}
|
||||
<PriceUnit price={total.nfts} size="small" /> assets with{' '}
|
||||
<PriceUnit price={total.datatokens} size="small" /> different datatokens.{' '}
|
||||
<PriceUnit price={total.veAllocated} symbol="veOCEAN" size="small" />{' '}
|
||||
allocated.{' '}
|
||||
<PriceUnit price={total.veLocked} symbol="OCEAN" size="small" /> locked.
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -6,4 +6,6 @@ export interface StatsTotal {
|
||||
nfts: number
|
||||
datatokens: number
|
||||
orders: number
|
||||
veAllocated: number
|
||||
veLocked: number
|
||||
}
|
||||
|
@ -14,11 +14,14 @@ import { useMarketMetadata } from '@context/MarketMetadata'
|
||||
import Tooltip from '@shared/atoms/Tooltip'
|
||||
import Markdown from '@shared/Markdown'
|
||||
import content from '../../../../content/footer.json'
|
||||
import { getTotalAllocatedAndLocked } from '@utils/veAllocation'
|
||||
|
||||
const initialTotal: StatsTotal = {
|
||||
nfts: 0,
|
||||
datatokens: 0,
|
||||
orders: 0
|
||||
orders: 0,
|
||||
veAllocated: 0,
|
||||
veLocked: 0
|
||||
}
|
||||
|
||||
export default function MarketStats(): ReactElement {
|
||||
@ -34,15 +37,15 @@ export default function MarketStats(): ReactElement {
|
||||
// Set the main chain ids we want to display stats for
|
||||
//
|
||||
useEffect(() => {
|
||||
if (!networksList || !appConfig || !appConfig?.chainIdsSupported) return
|
||||
if (!networksList || !appConfig || !appConfig?.chainIds) return
|
||||
|
||||
const mainChainIdsList = filterNetworksByType(
|
||||
'mainnet',
|
||||
appConfig.chainIdsSupported,
|
||||
appConfig.chainIds,
|
||||
networksList
|
||||
)
|
||||
setMainChainIds(mainChainIdsList)
|
||||
}, [appConfig, appConfig?.chainIdsSupported, networksList])
|
||||
}, [appConfig, appConfig?.chainIds, networksList])
|
||||
|
||||
//
|
||||
// Helper: fetch data from subgraph
|
||||
@ -68,6 +71,12 @@ export default function MarketStats(): ReactElement {
|
||||
LoggerInstance.error('Error fetching global stats: ', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
const veTotals = await getTotalAllocatedAndLocked()
|
||||
total.veAllocated = veTotals.totalAllocated
|
||||
total.veLocked = veTotals.totalLocked
|
||||
setTotal(total)
|
||||
|
||||
setData(newData)
|
||||
}, [mainChainIds])
|
||||
|
||||
@ -83,9 +92,7 @@ export default function MarketStats(): ReactElement {
|
||||
//
|
||||
useEffect(() => {
|
||||
if (!data || !mainChainIds?.length) return
|
||||
const newTotal: StatsTotal = {
|
||||
...initialTotal // always start calculating beginning from initial 0 values
|
||||
}
|
||||
const newTotal: StatsTotal = total
|
||||
|
||||
for (const chainId of mainChainIds) {
|
||||
try {
|
||||
|
@ -20,13 +20,13 @@ export default function Networks(): ReactElement {
|
||||
|
||||
const networksMain = filterNetworksByType(
|
||||
'mainnet',
|
||||
appConfig.chainIdsSupported,
|
||||
appConfig.chainIds,
|
||||
networksList
|
||||
)
|
||||
|
||||
const networksTest = filterNetworksByType(
|
||||
'testnet',
|
||||
appConfig.chainIdsSupported,
|
||||
appConfig.chainIds,
|
||||
networksList
|
||||
)
|
||||
|
||||
|
83
src/components/Home/SectionQueryResult.tsx
Normal file
83
src/components/Home/SectionQueryResult.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { useIsMounted } from '@hooks/useIsMounted'
|
||||
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
||||
import AssetList from '@shared/AssetList'
|
||||
import { queryMetadata } from '@utils/aquarius'
|
||||
import React, { ReactElement, useState, useEffect } from 'react'
|
||||
import styles from './index.module.css'
|
||||
|
||||
function sortElements(items: Asset[], sorted: string[]) {
|
||||
items.sort(function (a, b) {
|
||||
return sorted.indexOf(a.nftAddress) - sorted.indexOf(b.nftAddress)
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
||||
export default function SectionQueryResult({
|
||||
title,
|
||||
query,
|
||||
action,
|
||||
queryData
|
||||
}: {
|
||||
title: ReactElement | string
|
||||
query: SearchQuery
|
||||
action?: ReactElement
|
||||
queryData?: string[]
|
||||
}): ReactElement {
|
||||
const { chainIds } = useUserPreferences()
|
||||
const [result, setResult] = useState<PagedAssets>()
|
||||
const [loading, setLoading] = useState<boolean>()
|
||||
const isMounted = useIsMounted()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
useEffect(() => {
|
||||
if (!query) return
|
||||
|
||||
async function init() {
|
||||
if (chainIds.length === 0) {
|
||||
const result: PagedAssets = {
|
||||
results: [],
|
||||
page: 0,
|
||||
totalPages: 0,
|
||||
totalResults: 0,
|
||||
aggregations: undefined
|
||||
}
|
||||
setResult(result)
|
||||
setLoading(false)
|
||||
} else {
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
const result = await queryMetadata(query, newCancelToken())
|
||||
if (!isMounted()) return
|
||||
if (queryData && result?.totalResults > 0) {
|
||||
const sortedAssets = sortElements(result.results, queryData)
|
||||
const overflow = sortedAssets.length - 6
|
||||
sortedAssets.splice(sortedAssets.length - overflow, overflow)
|
||||
result.results = sortedAssets
|
||||
}
|
||||
setResult(result)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
LoggerInstance.error(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
init()
|
||||
}, [chainIds.length, isMounted, newCancelToken, query, queryData])
|
||||
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<h3>{title}</h3>
|
||||
|
||||
<AssetList
|
||||
assets={result?.results}
|
||||
showPagination={false}
|
||||
isLoading={loading || !query}
|
||||
/>
|
||||
|
||||
{action && action}
|
||||
</section>
|
||||
)
|
||||
}
|
@ -1,103 +1,24 @@
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import AssetList from '@shared/AssetList'
|
||||
import Button from '@shared/atoms/Button'
|
||||
import Bookmarks from './Bookmarks'
|
||||
import { generateBaseQuery, queryMetadata } from '@utils/aquarius'
|
||||
import { Asset, LoggerInstance } from '@oceanprotocol/lib'
|
||||
import { generateBaseQuery } from '@utils/aquarius'
|
||||
import { useUserPreferences } from '@context/UserPreferences'
|
||||
import { useIsMounted } from '@hooks/useIsMounted'
|
||||
import { useCancelToken } from '@hooks/useCancelToken'
|
||||
import { SortTermOptions } from '../../@types/aquarius/SearchQuery'
|
||||
import TopSales from './TopSales'
|
||||
import styles from './index.module.css'
|
||||
|
||||
function sortElements(items: Asset[], sorted: string[]) {
|
||||
items.sort(function (a, b) {
|
||||
return (
|
||||
sorted.indexOf(a.services[0].datatokenAddress.toLowerCase()) -
|
||||
sorted.indexOf(b.services[0].datatokenAddress.toLowerCase())
|
||||
)
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
||||
function SectionQueryResult({
|
||||
title,
|
||||
query,
|
||||
action,
|
||||
queryData
|
||||
}: {
|
||||
title: ReactElement | string
|
||||
query: SearchQuery
|
||||
action?: ReactElement
|
||||
queryData?: string[]
|
||||
}) {
|
||||
const { chainIds } = useUserPreferences()
|
||||
const [result, setResult] = useState<PagedAssets>()
|
||||
const [loading, setLoading] = useState<boolean>()
|
||||
const isMounted = useIsMounted()
|
||||
const newCancelToken = useCancelToken()
|
||||
|
||||
useEffect(() => {
|
||||
if (!query) return
|
||||
|
||||
async function init() {
|
||||
if (chainIds.length === 0) {
|
||||
const result: PagedAssets = {
|
||||
results: [],
|
||||
page: 0,
|
||||
totalPages: 0,
|
||||
totalResults: 0,
|
||||
aggregations: undefined
|
||||
}
|
||||
setResult(result)
|
||||
setLoading(false)
|
||||
} else {
|
||||
try {
|
||||
setLoading(true)
|
||||
const result = await queryMetadata(query, newCancelToken())
|
||||
if (!isMounted()) return
|
||||
if (queryData && result?.totalResults > 0) {
|
||||
const sortedAssets = sortElements(result.results, queryData)
|
||||
const overflow = sortedAssets.length - 9
|
||||
sortedAssets.splice(sortedAssets.length - overflow, overflow)
|
||||
result.results = sortedAssets
|
||||
}
|
||||
setResult(result)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
LoggerInstance.error(error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
init()
|
||||
}, [chainIds.length, isMounted, newCancelToken, query, queryData])
|
||||
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<h3>{title}</h3>
|
||||
|
||||
<AssetList
|
||||
assets={result?.results}
|
||||
showPagination={false}
|
||||
isLoading={loading || !query}
|
||||
/>
|
||||
|
||||
{action && action}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
import SectionQueryResult from './SectionQueryResult'
|
||||
|
||||
export default function HomePage(): ReactElement {
|
||||
const [queryLatest, setQueryLatest] = useState<SearchQuery>()
|
||||
const [queryMostSales, setQueryMostSales] = useState<SearchQuery>()
|
||||
const [queryMostAllocation, setQueryMostAllocation] = useState<SearchQuery>()
|
||||
const { chainIds } = useUserPreferences()
|
||||
|
||||
useEffect(() => {
|
||||
const baseParams = {
|
||||
chainIds,
|
||||
esPaginationOptions: {
|
||||
size: 9
|
||||
size: 6
|
||||
},
|
||||
sortOptions: {
|
||||
sortBy: SortTermOptions.Created
|
||||
@ -108,13 +29,23 @@ export default function HomePage(): ReactElement {
|
||||
const baseParamsSales = {
|
||||
chainIds,
|
||||
esPaginationOptions: {
|
||||
size: 9
|
||||
size: 6
|
||||
},
|
||||
sortOptions: {
|
||||
sortBy: SortTermOptions.Stats
|
||||
} as SortOptions
|
||||
} as BaseQueryParams
|
||||
setQueryMostSales(generateBaseQuery(baseParamsSales))
|
||||
const baseParamsAllocation = {
|
||||
chainIds,
|
||||
esPaginationOptions: {
|
||||
size: 6
|
||||
},
|
||||
sortOptions: {
|
||||
sortBy: SortTermOptions.Allocated
|
||||
} as SortOptions
|
||||
} as BaseQueryParams
|
||||
setQueryMostAllocation(generateBaseQuery(baseParamsAllocation))
|
||||
}, [chainIds])
|
||||
|
||||
return (
|
||||
@ -124,8 +55,15 @@ export default function HomePage(): ReactElement {
|
||||
<Bookmarks />
|
||||
</section>
|
||||
|
||||
<SectionQueryResult
|
||||
title="Highest veOCEAN Allocations"
|
||||
query={queryMostAllocation}
|
||||
/>
|
||||
|
||||
<SectionQueryResult title="Most Sales" query={queryMostSales} />
|
||||
|
||||
<TopSales title="Publishers With Most Sales" />
|
||||
|
||||
<SectionQueryResult
|
||||
title="Recently Published"
|
||||
query={queryLatest}
|
||||
@ -135,8 +73,6 @@ export default function HomePage(): ReactElement {
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
<TopSales title="Publishers With Most Sales" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -13,13 +13,13 @@ export default function AvailableNetworks(): ReactElement {
|
||||
|
||||
const networksMain = filterNetworksByType(
|
||||
'mainnet',
|
||||
appConfig.chainIdsSupported,
|
||||
appConfig.chainIds,
|
||||
networksList
|
||||
)
|
||||
|
||||
const networksTest = filterNetworksByType(
|
||||
'testnet',
|
||||
appConfig.chainIdsSupported,
|
||||
appConfig.chainIds,
|
||||
networksList
|
||||
)
|
||||
const networkCategories = [
|
||||
|
Loading…
Reference in New Issue
Block a user