1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

Merge pull request #30 from oceanprotocol/feature/v3

Remove the old, add the new Ocean
This commit is contained in:
Matthias Kretschmann 2020-07-15 22:30:55 +02:00 committed by GitHub
commit 74e2f95444
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 1364 additions and 1290 deletions

View File

@ -1,26 +1,15 @@
#Spree config GATSBY_INFURA_PROJECT_ID="xxx"
NODE_URI='http://localhost:8545'
AQUARIUS_URI='http://aquarius:5000'
BRIZO_URI='http://localhost:8030'
BRIZO_ADDRESS='0x068ed00cf0441e4829d9784fcbe7b9e26d4bd8d0'
SECRET_STORE_URI='http://localhost:12001'
FAUCET_URI='https://localhost:3001'
RATING_URI='http://localhost:8000'
#Nile market # Local config
#NODE_URI='https://nile.dev-ocean.com' GATSBY_NODE_URI='http://localhost:8545'
#AQUARIUS_URI='https://aquarius.nile.market.dev-ocean.com' GATSBY_METADATA_STORE_URI='http://aquarius:5000'
#BRIZO_URI='https://brizo.nile.market.dev-ocean.com' GATSBY_PROVIDER_URI='http://localhost:8030'
#BRIZO_ADDRESS='0xeD792C5FcC8bF3322a6ba89A6e51eF0B6fB3C530' GATSBY_FACTORY_ADDRESS='0xxxx'
#SECRET_STORE_URI='https://secret-store.nile.dev-ocean.com' #GATSBY_OCEAN_TOKEN_ADDRESS='0xxxx'
#FAUCET_URI='https://faucet.nile.dev-ocean.com'
#RATING_URI='https://rating.nile.market.dev-ocean.com'
#Pacific market # Rinkeby
#NODE_URI='https://pacific.oceanprotocol.com' #GATSBY_NODE_URI='https://rinkeby.infura.io/v3/GATSBY_INFURA_PROJECT_ID'
#AQUARIUS_URI='https://aquarius.pacific.market.dev-ocean.com' #GATSBY_METADATA_STORE_URI='https://aquarius.rinkeby.v3.dev-ocean.com'
#BRIZO_URI='https://brizo.pacific.market.dev-ocean.com' #GATSBY_PROVIDER_URI='https://provider.rinkeby.v3.dev-ocean.com'
#BRIZO_ADDRESS='0xeD792C5FcC8bF3322a6ba89A6e51eF0B6fB3C530' #GATSBY_FACTORY_ADDRESS='0xB9d406D24B310A7D821D0b782a36909e8c925471'
#SECRET_STORE_URI='https://secret-store.oceanprotocol.com' #GATSBY_OCEAN_TOKEN_ADDRESS='0xf6AE724aD6e6Fa89B6aBc9710C5eb692b7F57139'
#FAUCET_URI='https://faucet.oceanprotocol.com'
#RATING_URI='https://rating.pacific.market.dev-ocean.com'

View File

@ -174,13 +174,12 @@ vercel alias
## 🏗 Ocean Protocol Infrastructure ## 🏗 Ocean Protocol Infrastructure
The following Aquarius & Brizo instances specifically for marketplace are deployed in Ocean Protocol's AWS K8: The following Metadata Store & Provider instances specifically for marketplace are deployed in Ocean Protocol's AWS K8:
**Nile (Staging)** **Rinkeby (Staging)**
- K8 namespace: `market-nile` - `[aquarius.rinkeby.v3.dev-ocean.com](https://aquarius.rinkeby.v3.dev-ocean.com)`
- `aquarius.nile.market.dev-ocean.com` - `[provider.rinkeby.v3.dev-ocean.com](https://provider.rinkeby.v3.dev-ocean.com)`
- `brizo.nile.market.dev-ocean.com`
Edit command with `kubectl`, e.g.: Edit command with `kubectl`, e.g.:
@ -188,7 +187,7 @@ Edit command with `kubectl`, e.g.:
kubectl edit deployment -n market-nile aquarius kubectl edit deployment -n market-nile aquarius
``` ```
**Pacific (Production)** **Main (Production)**
- K8 namespace: `market-pacific` - K8 namespace: `market-pacific`
- `aquarius.pacific.market.dev-ocean.com` - `aquarius.pacific.market.dev-ocean.com`

View File

@ -1,19 +1,24 @@
module.exports = { module.exports = {
oceanConfig: { oceanConfig: {
nodeUri: process.env.NODE_URI || 'https://pacific.oceanprotocol.com', nodeUri:
aquariusUri: process.env.GATSBY_NODE_URI ||
process.env.AQUARIUS_URI || `https://rinkeby.infura.io/${process.env.GATSBY_INFURA_PROJECT_ID}`,
'https://aquarius.marketplace.oceanprotocol.com', metadataStoreUri:
brizoUri: process.env.GATSBY_METADATA_STORE_URI ||
process.env.BRIZO_URI || 'https://brizo.marketplace.oceanprotocol.com', 'https://aquarius.rinkeby.v3.dev-ocean.com',
brizoAddress: providerUri:
process.env.BRIZO_ADDRESS || '0x00c6A0BC5cD0078d6Cd0b659E8061B404cfa5704', process.env.GATSBY_PROVIDER_URI ||
secretStoreUri: 'https://provider.rinkeby.v3.dev-ocean.com',
process.env.SECRET_STORE_URI || 'https://secret-store.oceanprotocol.com', factoryAddress:
faucetUri: process.env.FAUCET_URI || 'https://faucet.oceanprotocol.com', process.env.GATSBY_FACTORY_ADDRESS ||
ratingUri: '0xB9d406D24B310A7D821D0b782a36909e8c925471',
process.env.RATING_URI || oceanTokenAddress:
'https://rating.pacific.marketplace.dev-ocean.com', process.env.GATSBY_OCEAN_TOKEN_ADDRESS ||
'0xf6AE724aD6e6Fa89B6aBc9710C5eb692b7F57139',
verbose: 3 verbose: 3
} },
// Main, Rinkeby, Kovan
// networks: [1, 4, 42],
networks: [4],
infuraProjectId: process.env.GATSBY_INFURA_PROJECT_ID || 'xxx'
} }

View File

@ -1,7 +1,7 @@
{ {
"site": { "site": {
"siteTitle": "Ocean Market", "siteTitle": "Ocean Market",
"siteTagline": "A marketplace to find and publish open data sets in the Ocean Network.", "siteTagline": "A marketplace to find, publish and trade data sets in the Ocean Network.",
"siteUrl": "https://market.oceanprotocol.now.sh", "siteUrl": "https://market.oceanprotocol.now.sh",
"siteIcon": "node_modules/@oceanprotocol/art/logo/favicon-white.png", "siteIcon": "node_modules/@oceanprotocol/art/logo/favicon-white.png",
"siteImage": "../src/images/share.png", "siteImage": "../src/images/share.png",

View File

@ -1,11 +1,14 @@
require('dotenv').config() require('dotenv').config()
const siteContent = require('./content/site.json') const siteContent = require('./content/site.json')
const { oceanConfig } = require('./app.config') const appConfig = require('./app.config')
module.exports = { module.exports = {
siteMetadata: { siteMetadata: {
...siteContent.site ...siteContent.site,
appConfig: {
...appConfig
}
}, },
plugins: [ plugins: [
{ {
@ -29,12 +32,6 @@ module.exports = {
path: `${__dirname}/node_modules/@oceanprotocol/art/` path: `${__dirname}/node_modules/@oceanprotocol/art/`
} }
}, },
{
resolve: 'gatsby-source-ocean',
options: {
aquariusUri: oceanConfig.aquariusUri
}
},
{ {
resolve: 'gatsby-plugin-sharp', resolve: 'gatsby-plugin-sharp',
options: { options: {

View File

@ -1,5 +1,3 @@
const path = require('path')
exports.onCreateWebpackConfig = ({ actions }) => { exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({ actions.setWebpackConfig({
node: { node: {
@ -9,60 +7,6 @@ exports.onCreateWebpackConfig = ({ actions }) => {
}) })
} }
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
// Create pages for all assets
const assetDetailsTemplate = path.resolve(
'src/components/templates/AssetDetails.tsx'
)
const result = await graphql(`
query {
allOceanAsset {
edges {
node {
did
main {
type
name
dateCreated
author
license
price
datePublished
files {
contentType
index
}
}
additionalInformation {
description
deliveryType
termsAndConditions
access
}
}
}
}
}
`)
if (result.errors) {
throw result.errors
}
await result.data.allOceanAsset.edges.forEach(({ node }) => {
const path = `/asset/${node.did}`
createPage({
path,
component: assetDetailsTemplate,
context: { did: node.did }
})
})
}
exports.onCreatePage = async ({ page, actions }) => { exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions const { createPage } = actions
// page.matchPath is a special key that's used for matching pages // page.matchPath is a special key that's used for matching pages

1106
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,20 +22,21 @@
"@loadable/component": "^5.13.1", "@loadable/component": "^5.13.1",
"@now/node": "^1.7.2", "@now/node": "^1.7.2",
"@oceanprotocol/art": "^3.0.0", "@oceanprotocol/art": "^3.0.0",
"@oceanprotocol/react": "0.0.11", "@oceanprotocol/lib": "^0.1.4",
"@oceanprotocol/squid": "^2.2.0", "@oceanprotocol/react": "^0.0.12",
"@oceanprotocol/typographies": "^0.1.0", "@oceanprotocol/typographies": "^0.1.0",
"@sindresorhus/slugify": "^1.0.0", "@sindresorhus/slugify": "^1.0.0",
"@tippyjs/react": "^4.1.0", "@tippyjs/react": "^4.1.0",
"@types/classnames": "^2.2.10", "@types/classnames": "^2.2.10",
"@walletconnect/web3-provider": "^1.0.15",
"axios": "^0.19.2", "axios": "^0.19.2",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"date-fns": "^2.14.0", "date-fns": "^2.14.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"ethereum-blockies": "github:MyEtherWallet/blockies", "ethereum-blockies": "github:MyEtherWallet/blockies",
"filesize": "^6.1.0", "filesize": "^6.1.0",
"formik": "^2.1.4", "formik": "^2.1.5",
"gatsby": "^2.24.2", "gatsby": "^2.24.3",
"gatsby-image": "^2.4.13", "gatsby-image": "^2.4.13",
"gatsby-plugin-manifest": "^2.4.18", "gatsby-plugin-manifest": "^2.4.18",
"gatsby-plugin-react-helmet": "^3.3.10", "gatsby-plugin-react-helmet": "^3.3.10",
@ -53,15 +54,14 @@
"numeral": "^2.0.6", "numeral": "^2.0.6",
"query-string": "^6.13.1", "query-string": "^6.13.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-data-table-component": "^6.9.6", "react-data-table-component": "^6.9.7",
"react-datepicker": "^3.1.3", "react-datepicker": "^3.1.3",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-dotdotdot": "^1.3.1", "react-dotdotdot": "^1.3.1",
"react-dropzone": "^11.0.1", "react-dropzone": "^11.0.2",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-markdown": "^4.3.1", "react-markdown": "^4.3.1",
"react-paginate": "^6.3.2", "react-paginate": "^6.3.2",
"react-rating": "^2.0.5",
"react-responsive-modal": "^5.0.2", "react-responsive-modal": "^5.0.2",
"react-spring": "^8.0.27", "react-spring": "^8.0.27",
"react-tabs": "^3.1.1", "react-tabs": "^3.1.1",
@ -73,17 +73,17 @@
"yup": "^0.29.1" "yup": "^0.29.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.10.3", "@babel/core": "^7.10.5",
"@babel/preset-typescript": "^7.10.1", "@babel/preset-typescript": "^7.10.1",
"@storybook/addon-actions": "^6.0.0-rc.3", "@storybook/addon-actions": "^6.0.0-rc.5",
"@storybook/addon-storyshots": "^6.0.0-rc.3", "@storybook/addon-storyshots": "^6.0.0-rc.5",
"@storybook/react": "^6.0.0-rc.3", "@storybook/react": "^6.0.0-rc.5",
"@svgr/webpack": "^5.4.0", "@svgr/webpack": "^5.4.0",
"@testing-library/jest-dom": "^5.11.0", "@testing-library/jest-dom": "^5.11.1",
"@testing-library/react": "^10.4.5", "@testing-library/react": "^10.4.7",
"@types/jest": "^26.0.4", "@types/jest": "^26.0.4",
"@types/loadable__component": "^5.13.0", "@types/loadable__component": "^5.13.0",
"@types/node": "^14.0.22", "@types/node": "^14.0.23",
"@types/numeral": "^0.0.28", "@types/numeral": "^0.0.28",
"@types/react": "^16.9.43", "@types/react": "^16.9.43",
"@types/react-datepicker": "^3.0.2", "@types/react-datepicker": "^3.0.2",
@ -92,8 +92,8 @@
"@types/react-tabs": "^2.3.2", "@types/react-tabs": "^2.3.2",
"@types/shortid": "0.0.29", "@types/shortid": "0.0.29",
"@types/yup": "^0.29.3", "@types/yup": "^0.29.3",
"@typescript-eslint/eslint-plugin": "^3.6.0", "@typescript-eslint/eslint-plugin": "^3.6.1",
"@typescript-eslint/parser": "^3.6.0", "@typescript-eslint/parser": "^3.6.1",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"babel-preset-react-app": "^9.1.2", "babel-preset-react-app": "^9.1.2",
"electron": "^9.1.0", "electron": "^9.1.0",

View File

@ -1,42 +0,0 @@
const axios = require('axios')
exports.sourceNodes = async (
{ actions, createNodeId, createContentDigest },
{ aquariusUri }
) => {
const { createNode } = actions
// Query for all assets to use in creating pages.
const result = await axios(`${aquariusUri}/api/v1/aquarius/assets`)
for (let i = 0; i < result.data.ids.length; i++) {
const did = result.data.ids[i]
const metadataResult = await axios(
`${aquariusUri}/api/v1/aquarius/assets/metadata/${did}`
)
const metadata = {
did,
...metadataResult.data.attributes
}
const nodeMeta = {
id: createNodeId(did),
parent: null,
children: [],
internal: {
type: 'OceanAsset',
contentDigest: createContentDigest(metadata),
description: `All data sets queried from ${aquariusUri}`
}
}
const node = {
...metadata,
...nodeMeta
}
await createNode(node)
}
}

View File

@ -1,3 +0,0 @@
{
"name": "gatsby-source-ocean"
}

View File

@ -1,5 +1,5 @@
import { File, MetaData, AdditionalInformation } from '@oceanprotocol/squid' import { File, MetaData, AdditionalInformation } from '@oceanprotocol/lib'
import { ServiceMetadata } from '@oceanprotocol/squid/dist/node/ddo/Service' import { ServiceMetadata } from '@oceanprotocol/lib/dist/node/ddo/Service'
export declare type AccessType = 'Download' | 'Compute' export declare type AccessType = 'Download' | 'Compute'
@ -34,8 +34,3 @@ export interface MetaDataPublishForm {
export interface ServiceMetaDataMarket extends ServiceMetadata { export interface ServiceMetaDataMarket extends ServiceMetadata {
attributes: MetaDataMarket attributes: MetaDataMarket
} }
// type for assets pulled into GraphQL
export interface OceanAsset extends MetaDataMarket {
did: DID
}

View File

@ -1,5 +1,7 @@
.app { .app {
height: 100%; height: 100%;
background: url('../../node_modules/@oceanprotocol/art/waves/waves.svg')
no-repeat center 10rem;
/* sticky footer technique */ /* sticky footer technique */
display: flex; display: flex;

View File

@ -12,6 +12,7 @@ export interface LayoutProps {
uri: string uri: string
description?: string description?: string
noPageHeader?: boolean noPageHeader?: boolean
headerCenter?: boolean
} }
export default function Layout({ export default function Layout({
@ -19,7 +20,8 @@ export default function Layout({
title, title,
uri, uri,
description, description,
noPageHeader noPageHeader,
headerCenter
}: LayoutProps): ReactElement { }: LayoutProps): ReactElement {
return ( return (
<div className={styles.app}> <div className={styles.app}>
@ -29,7 +31,11 @@ export default function Layout({
<main className={styles.main}> <main className={styles.main}>
<Container> <Container>
{title && !noPageHeader && ( {title && !noPageHeader && (
<PageHeader title={title} description={description} /> <PageHeader
title={title}
description={description}
center={headerCenter}
/>
)} )}
{children} {children}
</Container> </Container>

View File

@ -1,6 +1,9 @@
import React, { ReactElement, ReactNode } from 'react' import React, { ReactElement, ReactNode } from 'react'
import classNames from 'classnames/bind'
import styles from './Container.module.css' import styles from './Container.module.css'
const cx = classNames.bind(styles)
export default function Container({ export default function Container({
children, children,
narrow, narrow,
@ -10,13 +13,11 @@ export default function Container({
narrow?: boolean narrow?: boolean
className?: string className?: string
}): ReactElement { }): ReactElement {
return ( const styleClasses = cx({
<div container: true,
className={`${styles.container} ${narrow && styles.narrow} ${ narrow: narrow,
className && className [className]: className
}`} })
>
{children} return <div className={styleClasses}>{children}</div>
</div>
)
} }

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { File as FileMetaData } from '@oceanprotocol/squid' import { File as FileMetaData } from '@oceanprotocol/lib'
import filesize from 'filesize' import filesize from 'filesize'
import cleanupContentType from '../../utils/cleanupContentType' import cleanupContentType from '../../utils/cleanupContentType'
import styles from './File.module.css' import styles from './File.module.css'

View File

@ -1,57 +0,0 @@
.ratings {
display: flex;
margin-left: calc(var(--spacer) / -8);
font-weight: var(--font-weight-base);
}
/* Handle half stars our own way */
.ratings [style*='width:'] {
width: 100% !important;
clip-path: polygon(0 0, 60% 0, 60% 100%, 0% 100%);
}
.ratings [style*='width:100%'],
.ratings [style*='width: 100%'] {
width: 100% !important;
clip-path: none !important;
}
.ratings [style*='width:0%'],
.ratings [style*='width: 0%'] {
width: 0 !important;
clip-path: none !important;
}
.star {
margin-left: calc(var(--spacer) / 8);
}
.star svg {
fill: none;
stroke: var(--brand-grey);
}
.full {
composes: star;
}
.full svg {
fill: var(--color-primary);
stroke: var(--color-primary);
}
.ratingVotes {
display: inline-block;
font-size: var(--font-size-small);
padding-left: 5px;
color: var(--brand-grey);
}
.readonly {
composes: ratings;
}
.readonly .full svg {
fill: var(--color-secondary);
stroke: var(--color-secondary);
}

View File

@ -1,36 +0,0 @@
import React from 'react'
import Rating from './Rating'
export default {
title: 'Atoms/Rating'
}
export const Normal = () => (
<Rating
readonly
curation={{
rating: 3,
numVotes: 300
}}
/>
)
export const WithFraction = () => (
<Rating
readonly
curation={{
rating: 3.3,
numVotes: 300
}}
/>
)
export const Interactive = () => (
<Rating
onClick={(value: any) => null}
curation={{
rating: 3.3,
numVotes: 300
}}
/>
)

View File

@ -1,51 +0,0 @@
import React from 'react'
import ReactRating from 'react-rating'
import Star from '../../images/star.svg'
import { Curation } from '@oceanprotocol/squid'
import styles from './Rating.module.css'
export default function Rating({
curation,
readonly,
isLoading,
onClick
}: {
curation: Curation | undefined
readonly?: boolean
isLoading?: boolean
onClick?: (value: any) => void
}) {
let numVotes = 0
let rating = 0
if (!curation) return null
;({ numVotes, rating } = curation)
// if it's readonly then the fraction is 10 to show the average rating proper. When you select the rating you select from 1 to 5
const fractions = readonly ? 2 : 1
return (
<div className={`${readonly ? styles.readonly : styles.ratings}`}>
<ReactRating
emptySymbol={
<div className={styles.star}>
<Star />
</div>
}
fullSymbol={
<div className={styles.full}>
<Star />
</div>
}
initialRating={rating}
readonly={readonly || isLoading || false}
onClick={onClick}
fractions={fractions}
/>
<span className={styles.ratingVotes}>
{rating} {readonly ? `(${numVotes})` : ''}
</span>
</div>
)
}

View File

@ -7,14 +7,13 @@
.tag { .tag {
color: var(--color-secondary); color: var(--color-secondary);
font-size: var(--font-size-small); font-size: var(--font-size-small);
font-weight: var(--font-weight-bold); padding: 0.1rem 1.2rem 0.1rem 1.2rem;
padding: 0.2rem 1.2rem 0.2rem 1.2rem; margin-left: calc(var(--spacer) / 8);
margin-left: calc(var(--spacer) / 16); margin-right: calc(var(--spacer) / 8);
margin-right: calc(var(--spacer) / 16); margin-bottom: calc(var(--spacer) / 4);
margin-bottom: calc(var(--spacer) / 8);
text-align: center; text-align: center;
border-radius: var(--border-radius); border-radius: var(--border-radius);
border: 1px solid var(--brand-grey-light); border: 1px solid var(--brand-grey-lighter);
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -25,10 +24,6 @@
color: var(--color-primary); color: var(--color-primary);
} }
.tag:first-of-type {
margin-left: 0;
}
.more { .more {
font-size: var(--font-size-mini); font-size: var(--font-size-mini);
margin-left: calc(var(--spacer) / 8); margin-left: calc(var(--spacer) / 8);

View File

@ -30,6 +30,7 @@
.foot { .foot {
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
margin-top: calc(var(--spacer) / 2);
} }
.foot p { .foot p {

View File

@ -1,6 +1,6 @@
import AssetTeaser from '../molecules/AssetTeaser' import AssetTeaser from '../molecules/AssetTeaser'
import * as React from 'react' import * as React from 'react'
import { DDO } from '@oceanprotocol/squid' import { DDO } from '@oceanprotocol/lib'
import ddo from '../../../tests/unit/__fixtures__/ddo' import ddo from '../../../tests/unit/__fixtures__/ddo'
export default { export default {

View File

@ -2,10 +2,8 @@ import React from 'react'
import { Link } from 'gatsby' import { Link } from 'gatsby'
import Dotdotdot from 'react-dotdotdot' import Dotdotdot from 'react-dotdotdot'
import { MetaDataMarket } from '../../@types/MetaData' import { MetaDataMarket } from '../../@types/MetaData'
import Tags from '../atoms/Tags'
import Price from '../atoms/Price' import Price from '../atoms/Price'
import styles from './AssetTeaser.module.css' import styles from './AssetTeaser.module.css'
import Rating from '../atoms/Rating'
declare type AssetTeaserProps = { declare type AssetTeaserProps = {
did: string did: string
@ -16,17 +14,10 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
did, did,
metadata metadata
}: AssetTeaserProps) => { }: AssetTeaserProps) => {
if (!metadata.additionalInformation) return null
const { name, price } = metadata.main const { name, price } = metadata.main
const { description, access } = metadata.additionalInformation
const {
description,
copyrightHolder,
tags,
categories,
access
} = metadata.additionalInformation
const { curation } = metadata
return ( return (
<article className={styles.teaser}> <article className={styles.teaser}>
@ -35,20 +26,15 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
{access === 'Compute' && ( {access === 'Compute' && (
<div className={styles.accessLabel}>{access}</div> <div className={styles.accessLabel}>{access}</div>
)} )}
<Rating curation={curation} readonly />
<div className={styles.content}> <div className={styles.content}>
<Dotdotdot tagName="p" clamp={3}> <Dotdotdot tagName="p" clamp={3}>
{description || ''} {description || ''}
</Dotdotdot> </Dotdotdot>
{tags && (
<Tags className={styles.tags} items={tags} max={3} noLinks />
)}
</div> </div>
<footer className={styles.foot}> <footer className={styles.foot}>
<Price price={price} small /> <Price price={price} />
</footer> </footer>
</Link> </Link>
</article> </article>

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { useNavigate } from '@reach/router' import { useNavigate } from '@reach/router'
import { DDO } from '@oceanprotocol/squid' import { DDO } from '@oceanprotocol/lib'
import { redeploy } from '../../utils' import { redeploy } from '../../utils'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import BaseDialog from '../atoms/BaseDialog' import BaseDialog from '../atoms/BaseDialog'

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { File } from '@oceanprotocol/squid' import { File } from '@oceanprotocol/lib'
import { prettySize } from '../../../utils' import { prettySize } from '../../../utils'
import cleanupContentType from '../../../utils/cleanupContentType' import cleanupContentType from '../../../utils/cleanupContentType'
import styles from './Info.module.css' import styles from './Info.module.css'

View File

@ -14,3 +14,9 @@
margin-top: calc(var(--spacer) / 4); margin-top: calc(var(--spacer) / 4);
margin-bottom: 0; margin-bottom: 0;
} }
.center {
margin-left: auto;
margin-right: auto;
text-align: center;
}

View File

@ -6,14 +6,16 @@ const cx = classNames.bind(styles)
export default function PageHeader({ export default function PageHeader({
title, title,
description description,
center
}: { }: {
title: string title: string
description?: string description?: string
center?: boolean center?: boolean
}): ReactElement { }): ReactElement {
const styleClasses = cx({ const styleClasses = cx({
header: true header: true,
center: center
}) })
return ( return (

View File

@ -15,7 +15,7 @@
margin-top: -1px; margin-top: -1px;
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
border: 1px solid var(--brand-grey-light); border: 1px solid var(--brand-grey-lighter);
min-width: 3.5rem; min-width: 3.5rem;
} }

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect, ReactElement } from 'react'
import ReactPaginate from 'react-paginate' import ReactPaginate from 'react-paginate'
import styles from './Pagination.module.css' import styles from './Pagination.module.css'
@ -14,7 +14,9 @@ export default function Pagination({
currentPage, currentPage,
hrefBuilder, hrefBuilder,
onPageChange onPageChange
}: PaginationProps) { }: PaginationProps): ReactElement {
if (!totalPages || totalPages < 2) return null
const [smallViewport, setSmallViewport] = useState(true) const [smallViewport, setSmallViewport] = useState(true)
function viewportChange(mq: { matches: boolean }) { function viewportChange(mq: { matches: boolean }) {
@ -31,7 +33,7 @@ export default function Pagination({
} }
}, []) }, [])
return totalPages > 1 ? ( return (
<ReactPaginate <ReactPaginate
pageCount={totalPages} pageCount={totalPages}
// react-pagination starts counting at 0, we start at 1 // react-pagination starts counting at 0, we start at 1
@ -53,5 +55,5 @@ export default function Pagination({
disabledClassName={styles.prevNextDisabled} disabledClassName={styles.prevNextDisabled}
breakLinkClassName={styles.break} breakLinkClassName={styles.break}
/> />
) : null )
} }

View File

@ -1,7 +1,6 @@
import React, { useState, ChangeEvent, FormEvent, ReactElement } from 'react' import React, { useState, ChangeEvent, FormEvent, ReactElement } from 'react'
import { useNavigate } from '@reach/router' import { useNavigate } from '@reach/router'
import styles from './SearchBar.module.css' import styles from './SearchBar.module.css'
import Loader from '../atoms/Loader'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import Input from '../atoms/Input' import Input from '../atoms/Input'
import InputGroup from '../atoms/Input/InputGroup' import InputGroup from '../atoms/Input/InputGroup'
@ -19,7 +18,6 @@ export default function SearchBar({
}): ReactElement { }): ReactElement {
const navigate = useNavigate() const navigate = useNavigate()
const [value, setValue] = useState(initialValue || '') const [value, setValue] = useState(initialValue || '')
const [searchStarted, setSearchStarted] = useState(false)
function handleChange(e: ChangeEvent<HTMLInputElement>) { function handleChange(e: ChangeEvent<HTMLInputElement>) {
setValue(e.target.value) setValue(e.target.value)
@ -27,10 +25,7 @@ export default function SearchBar({
function startSearch(e: FormEvent<HTMLButtonElement>) { function startSearch(e: FormEvent<HTMLButtonElement>) {
e.preventDefault() e.preventDefault()
if (value === '') return if (value === '') return
setSearchStarted(true)
navigate(`/search?text=${value}`) navigate(`/search?text=${value}`)
} }
@ -46,7 +41,7 @@ export default function SearchBar({
required required
/> />
<Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}> <Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}>
{searchStarted ? <Loader /> : 'Search'} Search
</Button> </Button>
</InputGroup> </InputGroup>

View File

@ -1,15 +1,14 @@
import React from 'react' import React from 'react'
import styles from './Account.module.css' import styles from './Account.module.css'
import { useWeb3, useOcean } from '@oceanprotocol/react' import { useOcean } from '@oceanprotocol/react'
import { toDataUrl } from 'ethereum-blockies' import { toDataUrl } from 'ethereum-blockies'
import { ReactComponent as Caret } from '../../../images/caret.svg' import { ReactComponent as Caret } from '../../../images/caret.svg'
import Status from '../../atoms/Status' import Status from '../../atoms/Status'
import {
function accountTruncate(account: string) { accountTruncate,
const middle = account.substring(6, 38) connectWallet,
const truncated = account.replace(middle, '…') isCorrectNetwork
return truncated } from '../../../utils/wallet'
}
const Blockies = ({ account }: { account: string | undefined }) => { const Blockies = ({ account }: { account: string | undefined }) => {
if (!account) return null if (!account) return null
@ -28,15 +27,14 @@ const Blockies = ({ account }: { account: string | undefined }) => {
// Forward ref for Tippy.js // Forward ref for Tippy.js
// eslint-disable-next-line // eslint-disable-next-line
const Account = React.forwardRef((props, ref: any) => { const Account = React.forwardRef((props, ref: any) => {
const { account, web3Connect, ethProviderStatus } = useWeb3() const { accountId, status, connect, chainId } = useOcean()
const { status } = useOcean() const hasSuccess = status === 1 && isCorrectNetwork(chainId)
const hasSuccess = ethProviderStatus === 1 && status === 1
return account ? ( return accountId ? (
<button className={styles.button} aria-label="Account" ref={ref}> <button className={styles.button} aria-label="Account" ref={ref}>
<Blockies account={account} /> <Blockies account={accountId} />
<span className={styles.address} title={account}> <span className={styles.address} title={accountId}>
{accountTruncate(account)} {accountTruncate(accountId)}
</span> </span>
{!hasSuccess && ( {!hasSuccess && (
<Status className={styles.status} state="warning" aria-hidden /> <Status className={styles.status} state="warning" aria-hidden />
@ -46,7 +44,7 @@ const Account = React.forwardRef((props, ref: any) => {
) : ( ) : (
<button <button
className={styles.button} className={styles.button}
onClick={() => web3Connect.connect()} onClick={async () => await connectWallet(connect)}
// Need the `ref` here although we do not want // Need the `ref` here although we do not want
// the Tippy to show in this state. // the Tippy to show in this state.
ref={ref} ref={ref}

View File

@ -5,21 +5,31 @@
max-width: 25rem; max-width: 25rem;
} }
.details > ul {
margin-bottom: calc(var(--spacer) / 4);
}
.balance { .balance {
font-size: var(--font-size-base);
font-weight: var(--font-weight-bold);
color: var(--color-secondary); color: var(--color-secondary);
white-space: nowrap; white-space: nowrap;
font-size: var(--font-size-small);
} }
.balance span { .balance span {
font-size: var(--font-size-base); width: 20%;
font-weight: var(--font-weight-bold); text-align: right;
font-weight: var(--font-weight-base);
font-size: var(--font-size-small);
display: inline-block; display: inline-block;
margin-left: 0.1rem; margin-right: 0.4rem;
}
.actions {
border-top: 1px solid var(--brand-grey-lighter);
margin-top: calc(var(--spacer) / 2);
padding-top: calc(var(--spacer) / 2);
display: flex;
justify-content: space-between;
align-items: center;
font-size: var(--font-size-small);
color: var(--color-secondary);
} }
.arrow, .arrow,

View File

@ -1,30 +1,49 @@
import React, { ReactElement } from 'react' import React, { ReactElement, useEffect, useState } from 'react'
import Button from '../../atoms/Button' import Button from '../../atoms/Button'
import styles from './Details.module.css' import styles from './Details.module.css'
import { useWeb3, useOcean } from '@oceanprotocol/react' import { useOcean } from '@oceanprotocol/react'
import Web3Feedback from './Feedback' import Web3Feedback from './Feedback'
import { formatNumber } from '../../../utils' import { formatNumber } from '../../../utils'
import { connectWallet, getNetworkName } from '../../../utils/wallet'
import { getInjectedProviderName } from 'web3modal'
export default function Details({ attrs }: { attrs: any }): ReactElement { export default function Details({ attrs }: { attrs: any }): ReactElement {
const { balance, web3Connect } = useWeb3() const { ocean, balance, connect, logout, chainId } = useOcean()
const { balanceInOcean } = useOcean() const [balanceOcean, setBalanceOcean] = useState('0')
const ethBalanceText = formatNumber(Number(balance))
const oceanBalanceText = formatNumber(Number(balanceInOcean)) useEffect(() => {
async function init() {
if (!ocean) return
const accounts = await ocean.accounts.list()
const newBalanceOcean = await accounts[0].getOceanBalance()
newBalanceOcean && setBalanceOcean(newBalanceOcean)
}
init()
}, [ocean])
return ( return (
<div className={styles.details} {...attrs}> <div className={styles.details} {...attrs}>
<ul> <ul>
<li className={styles.balance}> <li className={styles.balance}>
OCEAN <span>{oceanBalanceText}</span> <span>OCEAN</span> {balanceOcean}
</li> </li>
<li className={styles.balance}> <li className={styles.balance}>
ETH <span>{ethBalanceText}</span> <span>ETH</span> {formatNumber(Number(balance))}
</li> </li>
<li> <li className={styles.actions}>
<span title="Connected provider">
{getInjectedProviderName()}
<br />
{getNetworkName(chainId)}
</span>
<Button <Button
style="text" style="text"
size="small" size="small"
onClick={() => web3Connect.toggleModal()} onClick={() => {
logout()
connectWallet(connect)
}}
> >
Switch Wallet Switch Wallet
</Button> </Button>

View File

@ -1,7 +1,9 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import Status from '../../atoms/Status' import Status from '../../atoms/Status'
import styles from './Feedback.module.css' import styles from './Feedback.module.css'
import { useWeb3, useOcean } from '@oceanprotocol/react' import { useOcean } from '@oceanprotocol/react'
import { isCorrectNetwork, getNetworkName } from '../../../utils/wallet'
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
export declare type Web3Error = { export declare type Web3Error = {
status: 'error' | 'warning' | 'success' status: 'error' | 'warning' | 'success'
@ -14,47 +16,46 @@ export default function Web3Feedback({
}: { }: {
isBalanceInsufficient?: boolean isBalanceInsufficient?: boolean
}): ReactElement { }): ReactElement {
const { ethProviderStatus } = useWeb3() const { appConfig } = useSiteMetadata()
const { status } = useOcean() const { account, status, chainId } = useOcean()
const isEthProviderAbsent = ethProviderStatus === -1
const isEthProviderDisconnected = ethProviderStatus === 0
const isOceanDisconnected = status === 0
const isOceanConnectionError = status === -1 const isOceanConnectionError = status === -1
const hasSuccess = ethProviderStatus === 1 && status === 1 const correctNetwork = isCorrectNetwork(chainId)
const showFeedback = !account || isOceanConnectionError || !correctNetwork
const allowedNetworkNames = appConfig.networks.map((network: number) =>
getNetworkName(network)
)
const state = isEthProviderAbsent const state = !account
? 'error' ? 'error'
: hasSuccess && !isBalanceInsufficient : !correctNetwork
? 'warning'
: account && !isBalanceInsufficient
? 'success' ? 'success'
: 'warning' : 'warning'
const title = isEthProviderAbsent const title = !account
? 'No Web3 Browser'
: isEthProviderDisconnected
? 'No account connected' ? 'No account connected'
: isOceanDisconnected
? 'Not connected to Pacific network'
: isOceanConnectionError : isOceanConnectionError
? 'Error connecting to Ocean' ? 'Error connecting to Ocean'
: hasSuccess : !correctNetwork
? 'Wrong Network'
: account
? isBalanceInsufficient === true ? isBalanceInsufficient === true
? 'Insufficient balance' ? 'Insufficient balance'
: 'Connected to Ocean' : 'Connected to Ocean'
: 'Something went wrong' : 'Something went wrong'
const message = isEthProviderAbsent const message = !account
? 'To download data sets you need a browser with Web3 capabilties, like Firefox with MetaMask installed.'
: isEthProviderDisconnected
? 'Please connect your Web3 wallet.' ? 'Please connect your Web3 wallet.'
: isOceanDisconnected
? 'Please connect in MetaMask to custom RPC https://pacific.oceanprotocol.com.'
: isOceanConnectionError : isOceanConnectionError
? 'Try again.' ? 'Please try again.'
: !correctNetwork
? `Please connect to ${allowedNetworkNames}.`
: isBalanceInsufficient === true : isBalanceInsufficient === true
? 'You do not have enough OCEAN in your wallet to purchase this asset.' ? 'You do not have enough OCEAN in your wallet to purchase this asset.'
: 'Something went wrong.' : 'Something went wrong.'
return !hasSuccess ? ( return showFeedback ? (
<section className={styles.feedback}> <section className={styles.feedback}>
<Status state={state} aria-hidden /> <Status state={state} aria-hidden />
<h3 className={styles.title}>{title}</h3> <h3 className={styles.title}>{title}</h3>

View File

@ -1,10 +1,10 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import loadable from '@loadable/component' import loadable from '@loadable/component'
import { useSpring, animated } from 'react-spring' import { useSpring, animated } from 'react-spring'
import { useWeb3 } from '@oceanprotocol/react'
import Account from './Account' import Account from './Account'
import Details from './Details' import Details from './Details'
import styles from './index.module.css' import styles from './index.module.css'
import { useOcean } from '@oceanprotocol/react'
const Tippy = loadable(() => import('@tippyjs/react/headless')) const Tippy = loadable(() => import('@tippyjs/react/headless'))
@ -15,7 +15,7 @@ const animation = {
} }
export default function Wallet(): ReactElement { export default function Wallet(): ReactElement {
const { account, ethProviderStatus } = useWeb3() const { accountId } = useOcean()
const [props, setSpring] = useSpring(() => animation.from) const [props, setSpring] = useSpring(() => animation.from)
function onMount() { function onMount() {
@ -34,14 +34,12 @@ export default function Wallet(): ReactElement {
}) })
} }
const isEthProviderAbsent = ethProviderStatus === -1
if (isEthProviderAbsent) return null
return ( return (
<Tippy <Tippy
interactive interactive
interactiveBorder={30} interactiveBorder={30}
trigger="click focus" trigger="click focus"
zIndex={1}
render={(attrs: any) => ( render={(attrs: any) => (
<animated.div style={props}> <animated.div style={props}>
<Details attrs={attrs} /> <Details attrs={attrs} />
@ -53,7 +51,7 @@ export default function Wallet(): ReactElement {
animation animation
onMount={onMount} onMount={onMount}
onHide={onHide} onHide={onHide}
disabled={!account} disabled={!accountId}
fallback={<Account />} fallback={<Account />}
> >
<Account /> <Account />

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import Compute from './Compute' import Compute from './Compute'
import ddo from '../../../../tests/unit/__fixtures__/ddo' import ddo from '../../../../tests/unit/__fixtures__/ddo'
import web3Mock from '../../../../tests/unit/__mocks__/web3' import web3Mock from '../../../../tests/unit/__mocks__/web3'
import squidMock from '../../../../tests/unit/__mocks__/@oceanprotocol/squid' import squidMock from '../../../../tests/unit/__mocks__/@oceanprotocol/lib'
import { context } from '../../../../tests/unit/__mocks__/web3provider' import { context } from '../../../../tests/unit/__mocks__/web3provider'
export default { export default {

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, ReactElement } from 'react' import React, { useState, useEffect, ReactElement } from 'react'
import { Ocean } from '@oceanprotocol/squid' import { Ocean } from '@oceanprotocol/lib'
import { fromWei } from 'web3-utils' import { fromWei } from 'web3-utils'
import compareAsBN, { Comparisson } from '../../../utils/compareAsBN' import compareAsBN, { Comparisson } from '../../../utils/compareAsBN'
import Loader from '../../atoms/Loader' import Loader from '../../atoms/Loader'

View File

@ -13,7 +13,7 @@ export default function AssetActions({
metadata: MetaDataMarket metadata: MetaDataMarket
did: string did: string
}): ReactElement { }): ReactElement {
const { ocean, balanceInOcean } = useOcean() // const { ocean, balanceInOcean } = useOcean()
const { access } = metadata.additionalInformation const { access } = metadata.additionalInformation
const isCompute = access && access === 'Compute' const isCompute = access && access === 'Compute'
@ -26,12 +26,13 @@ export default function AssetActions({
<div className={styles.tabContent}> <div className={styles.tabContent}>
<TabPanel> <TabPanel>
{isCompute ? ( {isCompute ? (
<Compute // <Compute
did={did} // did={did}
metadata={metadata} // metadata={metadata}
ocean={ocean} // ocean={ocean}
balance={balanceInOcean} // balance={balanceInOcean}
/> // />
'Compute Me'
) : ( ) : (
<Consume did={did} metadata={metadata} /> <Consume did={did} metadata={metadata} />
)} )}

View File

@ -1,9 +1,6 @@
.metaFull { .metaFull {
margin-top: calc(var(--spacer) * 2); margin-top: calc(var(--spacer) * 2);
font-size: var(--font-size-small); font-size: var(--font-size-small);
padding: var(--spacer);
border-radius: var(--border-radius);
background: var(--brand-grey-dimmed);
display: grid; display: grid;
gap: var(--spacer); gap: var(--spacer);
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;

View File

@ -12,35 +12,14 @@ export default function MetaFull({
metadata: MetaDataMarket metadata: MetaDataMarket
}): ReactElement { }): ReactElement {
const { dateCreated, datePublished, author, license } = metadata.main const { dateCreated, datePublished, author, license } = metadata.main
let dateRange const { categories } = metadata.additionalInformation
if (metadata && metadata.additionalInformation) {
;({ dateRange } = metadata.additionalInformation)
}
// In practice dateRange will always be defined, but in the rare case it isn't
// we put something to prevent errors
if (!dateRange) {
dateRange = [dateCreated, dateCreated]
}
return ( return (
<div className={styles.metaFull}> <div className={styles.metaFull}>
<MetaItem title="Author" content={author} /> <MetaItem title="Author" content={author} />
<MetaItem title="License" content={license} /> <MetaItem title="License" content={license} />
<MetaItem <MetaItem title="Category" content={categories[0]} />
title="Data Created" <MetaItem title="Data Created" content={<Time date={dateCreated} />} />
content={
dateRange && dateRange[0] !== dateRange[1] ? (
<>
<Time date={dateRange[0]} />
{' '}
<Time date={dateRange[1]} />
</>
) : (
<Time date={dateRange[0]} />
)
}
/>
<MetaItem <MetaItem
title="Data Published" title="Data Published"

View File

@ -1,4 +1,4 @@
import React, { ReactElement } from 'react' import React, { ReactElement, ReactNode } from 'react'
import styles from './MetaItem.module.css' import styles from './MetaItem.module.css'
export default function MetaItem({ export default function MetaItem({
@ -6,7 +6,7 @@ export default function MetaItem({
content content
}: { }: {
title: string title: string
content: any content: ReactNode
}): ReactElement { }): ReactElement {
return ( return (
<div className={styles.metaItem}> <div className={styles.metaItem}>

View File

@ -12,6 +12,5 @@
} }
.samples { .samples {
composes: box from '../../atoms/Box.module.css';
margin-top: calc(var(--spacer) / 2); margin-top: calc(var(--spacer) / 2);
} }

View File

@ -4,16 +4,14 @@ import { ListItem } from '../../atoms/Lists'
import MetaItem from './MetaItem' import MetaItem from './MetaItem'
import styles from './MetaSecondary.module.css' import styles from './MetaSecondary.module.css'
import { MetaDataMarket } from '../../../@types/MetaData' import { MetaDataMarket } from '../../../@types/MetaData'
import Tags from '../../atoms/Tags'
export default function MetaSecondary({ export default function MetaSecondary({
metadata metadata
}: { }: {
metadata: MetaDataMarket metadata: MetaDataMarket
}): ReactElement { }): ReactElement {
let links const { links, tags } = metadata.additionalInformation
if (metadata && metadata.additionalInformation) {
;({ links } = metadata.additionalInformation)
}
return ( return (
<aside className={styles.metaSecondary}> <aside className={styles.metaSecondary}>
@ -33,6 +31,8 @@ export default function MetaSecondary({
/> />
</div> </div>
)} )}
{tags && tags.length > 0 && <Tags items={tags} />}
</aside> </aside>
) )
} }

View File

@ -1,25 +0,0 @@
.rating {
composes: box from '../../atoms/Box.module.css';
display: flex;
flex-wrap: wrap;
flex-direction: column;
margin-top: 5px;
padding: 20px !important;
}
.rating svg {
width: var(--font-size-h3);
height: var(--font-size-h3);
}
.title {
font-size: var(--font-size-large);
}
.success {
background-color: var(--green);
}
.error {
background-color: var(--red);
}

View File

@ -1,65 +0,0 @@
import React, { useState, useEffect, ReactElement } from 'react'
import { toast } from 'react-toastify'
import Rating from '../../atoms/Rating'
import rateAsset from '../../../utils/rateAsset'
import { DID } from '@oceanprotocol/squid'
import styles from './RatingAction.module.css'
import getAssetRating from '../../../utils/getAssetRating'
import Loader from '../../atoms/Loader'
import { useWeb3 } from '@oceanprotocol/react'
export default function RatingAction({
did,
onVote
}: {
did: DID | string
onVote: () => void
}): ReactElement {
const { web3, account } = useWeb3()
const [rating, setRating] = useState<number>(0)
const [isloading, setIsLoading] = useState(false)
useEffect(() => {
async function getOwnRating() {
if (!account) return
const currentRating = await getAssetRating(did, account)
currentRating && setRating(currentRating.vote)
}
getOwnRating()
}, [account])
async function handleRatingClick(value: number) {
if (!web3) return
setIsLoading(true)
try {
const response = await rateAsset(did, web3, value)
if (!response) return
onVote()
setRating(value)
toast.success('Thank you for rating!', {
className: styles.success
})
} catch (error) {
toast.error(`There was an error: ${error.message}`, {
className: styles.error
})
}
setIsLoading(false)
}
return account ? (
<aside className={styles.rating}>
<h3 className={styles.title}>Review this data</h3>
{isloading ? (
<Loader />
) : (
<Rating
curation={{ rating: rating, numVotes: 0 }}
isLoading={isloading}
onClick={handleRatingClick}
/>
)}
</aside>
) : null
}

View File

@ -1,13 +1,17 @@
.grid { .grid {
display: grid; display: grid;
gap: calc(var(--spacer) * 1.5); gap: calc(var(--spacer) * 2);
position: relative; position: relative;
margin-top: -1.5rem; margin-top: -1.5rem;
} }
.content {
composes: box from '../../atoms/Box.module.css';
margin-top: var(--spacer);
}
@media (min-width: 60rem) { @media (min-width: 60rem) {
.grid { .grid {
gap: calc(var(--spacer) * 3);
/* lazy golden ratio */ /* lazy golden ratio */
grid-template-columns: 1.618fr minmax(0, 1fr); grid-template-columns: 1.618fr minmax(0, 1fr);
} }
@ -15,7 +19,7 @@
.sticky { .sticky {
position: sticky; position: sticky;
top: calc(var(--spacer) / 2); top: calc(var(--spacer) / 2);
margin-top: calc(var(--spacer) * 1.5); margin-top: var(--spacer);
} }
} }

View File

@ -1,10 +1,8 @@
import { MetaDataMarket } from '../../../@types/MetaData' import { MetaDataMarket } from '../../../@types/MetaData'
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { useOcean } from '@oceanprotocol/react'
import Time from '../../atoms/Time' import Time from '../../atoms/Time'
import { Link } from 'gatsby' import { Link } from 'gatsby'
import Markdown from '../../atoms/Markdown' import Markdown from '../../atoms/Markdown'
import Tags from '../../atoms/Tags'
import MetaFull from './MetaFull' import MetaFull from './MetaFull'
import MetaSecondary from './MetaSecondary' import MetaSecondary from './MetaSecondary'
import styles from './index.module.css' import styles from './index.module.css'
@ -21,42 +19,24 @@ export default function AssetContent({
did did
}: AssetContentProps): ReactElement { }: AssetContentProps): ReactElement {
const { datePublished } = metadata.main const { datePublished } = metadata.main
const { description, categories, tags } = metadata.additionalInformation const { description, categories } = metadata.additionalInformation
// const { curation } = metadata
// const { getCuration } = useMetadata()
// const [rating, setRating] = useState<number>(curation ? curation.rating : 0)
// const [numVotes, setNumVotes] = useState<number>(
// curation ? curation.numVotes : 0
// )
// const onVoteUpdate = async () => {
// const { rating, numVotes } = await getCuration(did)
// setRating(rating)
// setNumVotes(numVotes)
// }
return ( return (
<article className={styles.grid}> <article className={styles.grid}>
<div> <div className={styles.content}>
<aside className={styles.meta}> <aside className={styles.meta}>
<p>{datePublished && <Time date={datePublished} />}</p> <p>{datePublished && <Time date={datePublished} />}</p>
{categories && ( {categories && (
<p> <p>
<Link to={`/search?categories=["${categories[0]}"]`}> <Link to={`/search?categories=["${categories[0]}"]`}>
<a>{categories[0]}</a> {categories[0]}
</Link> </Link>
</p> </p>
)} )}
{/* <Rating curation={{ rating, numVotes }} readonly /> */}
</aside> </aside>
<Markdown text={description || ''} /> <Markdown text={description || ''} />
{tags && tags.length > 0 && <Tags items={tags} />}
<MetaSecondary metadata={metadata} /> <MetaSecondary metadata={metadata} />
<MetaFull did={did} metadata={metadata} /> <MetaFull did={did} metadata={metadata} />
@ -74,8 +54,6 @@ export default function AssetContent({
<div> <div>
<div className={styles.sticky}> <div className={styles.sticky}>
<AssetActions metadata={metadata} did={did} /> <AssetActions metadata={metadata} did={did} />
{/* <RatingAction did={did} onVote={onVoteUpdate} /> */}
</div> </div>
</div> </div>
</article> </article>

View File

@ -10,3 +10,9 @@
gap: var(--spacer); gap: var(--spacer);
} }
} }
.empty {
color: var(--color-secondary);
font-size: var(--font-size-small);
font-style: italic;
}

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import { DDO } from '@oceanprotocol/squid' import { DDO } from '@oceanprotocol/lib'
import AssetList from './AssetList' import AssetList from './AssetList'
import asset from '../../../tests/unit/__fixtures__/ddo' import asset from '../../../tests/unit/__fixtures__/ddo'

View File

@ -1,64 +1,77 @@
import AssetTeaser from '../molecules/AssetTeaser' import AssetTeaser from '../molecules/AssetTeaser'
import React from 'react' import React from 'react'
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius' import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatastore/MetadataStore'
import shortid from 'shortid' import { useLocation, useNavigate } from '@reach/router'
import Pagination from '../molecules/Pagination' import Pagination from '../molecules/Pagination'
import { updateQueryStringParameter } from '../../utils' import { updateQueryStringParameter } from '../../utils'
import styles from './AssetList.module.css' import styles from './AssetList.module.css'
import { MetaDataMarket } from '../../@types/MetaData' import { MetaDataMarket } from '../../@types/MetaData'
import { DDO } from '@oceanprotocol/squid' import { DDO } from '@oceanprotocol/lib'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
declare type AssetListProps = { declare type AssetListProps = {
queryResult: QueryResult queryResult: QueryResult
} }
const AssetList: React.FC<AssetListProps> = ({ queryResult }) => { const AssetList: React.FC<AssetListProps> = ({ queryResult }) => {
const { appConfig } = useSiteMetadata()
const location = useLocation()
const navigate = useNavigate()
// Construct the urls on the pagination links. This is only for UX, // Construct the urls on the pagination links. This is only for UX,
// since the links are no <Link> they will not work by itself. // since the links are no <Link> they will not work by itself.
// function hrefBuilder(pageIndex: number) { function hrefBuilder(pageIndex: number) {
// const newUrl = updateQueryStringParameter( const newUrl = updateQueryStringParameter(
// router.asPath, location.pathname + location.search,
// 'page', 'page',
// `${pageIndex}` `${pageIndex}`
// ) )
// return newUrl return newUrl
// } }
// // This is what iniitates a new search with new `page` // // This is what iniitates a new search with new `page`
// // url parameter // // url parameter
// function onPageChange(selected: number) { function onPageChange(selected: number) {
// const newUrl = updateQueryStringParameter( const newUrl = updateQueryStringParameter(
// router.asPath, location.pathname + location.search,
// 'page', 'page',
// `${selected + 1}` `${selected + 1}`
// ) )
// return router.push(newUrl) return navigate(newUrl)
// } }
return ( return (
<> <>
<div className={styles.assetList}> <div className={styles.assetList}>
{queryResult && {queryResult && queryResult.totalResults > 0 ? (
queryResult.results.map((ddo: DDO) => { queryResult.results.map((ddo: DDO) => {
const { attributes }: MetaDataMarket = new DDO( const { attributes }: MetaDataMarket = ddo.findServiceByType(
ddo 'metadata'
).findServiceByType('metadata') )
return ( return (
<AssetTeaser <AssetTeaser did={ddo.id} metadata={attributes} key={ddo.id} />
did={ddo.id}
metadata={attributes}
key={shortid.generate()}
/>
) )
})} })
) : (
<div className={styles.empty}>
No results found in {appConfig.oceanConfig.metadataStoreUri}
</div>
)}
</div> </div>
{/* <Pagination
totalPages={queryResult.totalPages} {/*
currentPage={queryResult.page} Little hack cause the pagination navigation only works
hrefBuilder={hrefBuilder} on the search page right now.
onPageChange={onPageChange} */}
/> */} {location.pathname === '/search' && queryResult && (
<Pagination
totalPages={queryResult.totalPages}
currentPage={queryResult.page}
hrefBuilder={hrefBuilder}
onPageChange={onPageChange}
/>
)}
</> </>
) )
} }

View File

@ -10,7 +10,7 @@ import Price from '../atoms/Price'
import { fromWei } from 'web3-utils' import { fromWei } from 'web3-utils'
import DateCell from '../atoms/Table/DateCell' import DateCell from '../atoms/Table/DateCell'
import DdoLinkCell from '../atoms/Table/DdoLinkCell' import DdoLinkCell from '../atoms/Table/DdoLinkCell'
import { MetaDataMain } from '@oceanprotocol/squid' import { MetaDataMain } from '@oceanprotocol/lib'
const consumedColumns = [ const consumedColumns = [
{ {

View File

@ -1,3 +1,2 @@
.header { .header {
background-color: var(--brand-white);
} }

View File

@ -11,7 +11,7 @@ import Price from '../atoms/Price'
import { fromWei } from 'web3-utils' import { fromWei } from 'web3-utils'
import Table from '../atoms/Table' import Table from '../atoms/Table'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import { MetaDataMain, Logger } from '@oceanprotocol/squid' import { MetaDataMain, Logger } from '@oceanprotocol/lib'
import DateCell from '../atoms/Table/DateCell' import DateCell from '../atoms/Table/DateCell'
import DdoLinkCell from '../atoms/Table/DdoLinkCell' import DdoLinkCell from '../atoms/Table/DdoLinkCell'
import shortid from 'shortid' import shortid from 'shortid'

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState, ReactElement } from 'react' import React, { useEffect, useState, ReactElement } from 'react'
import Loader from '../atoms/Loader' import Loader from '../atoms/Loader'
import { MetaDataMain } from '@oceanprotocol/squid' import { MetaDataMain } from '@oceanprotocol/lib'
import { import {
useOcean, useOcean,
OceanConnectionStatus, OceanConnectionStatus,

View File

@ -15,3 +15,8 @@
top: var(--spacer); top: var(--spacer);
} }
} }
.content {
composes: box from '../atoms/Box.module.css';
margin-top: var(--spacer);
}

View File

@ -8,15 +8,15 @@ import JobsList from '../organisms/JobsList'
const sections = [ const sections = [
{ {
title: 'Published', title: 'Published',
component: <PublishedList /> component: 'Coming Soon...'
}, },
{ {
title: 'Downloaded', title: 'Downloaded',
component: <ConsumedList /> component: 'Coming Soon...'
}, },
{ {
title: 'Compute Jobs', title: 'Compute Jobs',
component: <JobsList /> component: 'Coming Soon...'
} }
] ]
@ -32,7 +32,7 @@ const Section = ({ title, component }: { title: string; component: any }) => {
const HistoryPage: React.FC = () => { const HistoryPage: React.FC = () => {
return ( return (
<article className={styles.grid}> <article className={styles.grid}>
<div> <div className={styles.content}>
{sections.map((section) => { {sections.map((section) => {
const { title, component } = section const { title, component } = section
return <Section key={title} title={title} component={component} /> return <Section key={title} title={title} component={component} />

View File

@ -1,3 +1,14 @@
.grid { .searchWrap {
composes: assetList from '../organisms/AssetList.module.css'; display: flex;
justify-content: center;
margin-top: calc(var(--spacer) * var(--line-height));
}
.latest {
margin-top: calc(var(--spacer) * 2);
}
.latest h3 {
font-size: var(--font-size-large);
color: var(--color-secondary);
} }

View File

@ -1,31 +1,60 @@
import React, { ReactElement } from 'react' import React, { ReactElement, useEffect, useState } from 'react'
import SearchBar from '../molecules/SearchBar' import SearchBar from '../molecules/SearchBar'
import shortid from 'shortid'
import { OceanAsset } from '../../@types/MetaData'
import AssetTeaser from '../molecules/AssetTeaser'
import styles from './Home.module.css' import styles from './Home.module.css'
import { MetadataStore, Logger } from '@oceanprotocol/lib'
import AssetList from '../organisms/AssetList'
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatastore/MetadataStore'
import Container from '../atoms/Container'
import Loader from '../atoms/Loader'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
async function getLatestAssets(metadataStoreUri: string) {
try {
const metadataStore = new MetadataStore(metadataStoreUri, Logger)
const result = await metadataStore.queryMetadata({
page: 1,
offset: 10,
query: {},
sort: { created: -1 }
})
return result
} catch (error) {
console.error(error.message)
}
}
export default function HomePage(): ReactElement {
const { appConfig } = useSiteMetadata()
const [queryResult, setQueryResult] = useState<QueryResult>()
const [loading, setLoading] = useState(true)
useEffect(() => {
async function init() {
const results = await getLatestAssets(
appConfig.oceanConfig.metadataStoreUri
)
setQueryResult(results)
setLoading(false)
}
init()
}, [])
export default function HomePage({
assets
}: {
assets: {
node: OceanAsset
}[]
}): ReactElement {
return ( return (
<> <>
<SearchBar large /> <Container narrow className={styles.searchWrap}>
{assets && ( <SearchBar large />
<div className={styles.grid}> </Container>
{assets.map(({ node }: { node: OceanAsset }) => (
<AssetTeaser <section className={styles.latest}>
key={shortid.generate()} <h3>Latest Data Sets</h3>
did={node.did} {loading ? (
metadata={node} <Loader />
/> ) : (
))} queryResult && <AssetList queryResult={queryResult} />
</div> )}
)} </section>
</> </>
) )
} }

View File

@ -7,7 +7,7 @@ import { useOcean } from '@oceanprotocol/react'
import { import {
Service, Service,
ServiceCompute ServiceCompute
} from '@oceanprotocol/squid/dist/node/ddo/Service' } from '@oceanprotocol/lib/dist/node/ddo/Service'
import { Formik, Form as FormFormik, Field } from 'formik' import { Formik, Form as FormFormik, Field } from 'formik'
import Input from '../../atoms/Input' import Input from '../../atoms/Input'
import Button from '../../atoms/Button' import Button from '../../atoms/Button'
@ -15,7 +15,7 @@ import { transformPublishFormToMetadata } from './utils'
import { FormContent, FormFieldProps } from '../../../@types/Form' import { FormContent, FormFieldProps } from '../../../@types/Form'
import { MetaDataPublishForm } from '../../../@types/MetaData' import { MetaDataPublishForm } from '../../../@types/MetaData'
import AssetModel from '../../../models/Asset' import AssetModel from '../../../models/Asset'
import { File } from '@oceanprotocol/squid' import { File } from '@oceanprotocol/lib'
const validationSchema = Yup.object().shape<MetaDataPublishForm>({ const validationSchema = Yup.object().shape<MetaDataPublishForm>({
// ---- required fields ---- // ---- required fields ----
@ -81,7 +81,7 @@ export default function PublishForm({
// account, // account,
// metadata.main.price, // metadata.main.price,
// // Note: a hack without consequences. // // Note: a hack without consequences.
// // Will make metadata.main.datePublished (automatically created by Aquarius) // // Will make metadata.main.datePublished (automatically created by MetadataStore)
// // go out of sync with this service.main.datePublished. // // go out of sync with this service.main.datePublished.
// toStringNoMS(new Date(Date.now())) // toStringNoMS(new Date(Date.now()))
// ) // )

View File

@ -1,41 +0,0 @@
import React, { ReactElement } from 'react'
import { PageProps, graphql } from 'gatsby'
import Layout from '../Layout'
import AssetContent from '../organisms/AssetContent'
export default function AssetDetailsTemplate(props: PageProps): ReactElement {
const { oceanAsset } = props.data as any
return (
<Layout title={oceanAsset.main.name} uri={props.path}>
<AssetContent did={oceanAsset.did} metadata={oceanAsset} />
</Layout>
)
}
export const templateQuery = graphql`
query OceanAssetByDid($did: String!) {
oceanAsset(did: { eq: $did }) {
did
main {
type
name
dateCreated
author
license
price
datePublished
files {
index
contentType
}
}
additionalInformation {
description
deliveryType
termsAndConditions
access
}
}
}
`

View File

@ -1,8 +1,3 @@
.empty {
color: var(--color-secondary);
font-style: italic;
}
.grid { .grid {
display: grid; display: grid;
} }
@ -10,17 +5,21 @@
@media (min-width: 55rem) { @media (min-width: 55rem) {
.grid { .grid {
grid-column-gap: calc(var(--spacer) * 3); grid-column-gap: calc(var(--spacer) * 3);
grid-template-columns: minmax(0, 1fr) 3fr; grid-template-columns: minmax(0, 3fr) 1fr;
grid-template-areas: grid-template-areas:
'side search' 'search side'
'side results'; 'results side';
} }
.search { .search {
grid-area: search; grid-area: search;
} }
.side { .side {
grid-area: side; grid-area: side;
margin-top: calc(var(--spacer) * 2.5);
} }
.results { .results {
grid-area: results; grid-area: results;
} }

View File

@ -1,12 +1,12 @@
import React, { ReactElement, useState, useEffect } from 'react' import React, { ReactElement, useState, useEffect } from 'react'
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius' import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatastore/MetadataStore'
import SearchBar from '../../molecules/SearchBar' import SearchBar from '../../molecules/SearchBar'
import AssetList from '../../organisms/AssetList' import AssetList from '../../organisms/AssetList'
import { SearchPriceFilter } from '../../molecules/SearchPriceFilter' import { SearchPriceFilter } from '../../molecules/SearchPriceFilter'
import styles from './index.module.css' import styles from './index.module.css'
import queryString from 'query-string' import queryString from 'query-string'
import { getResults } from './utils' import { getResults } from './utils'
import Loader from '../../atoms/Loader'
export declare type SearchPageProps = { export declare type SearchPageProps = {
text: string | string[] text: string | string[]
@ -20,16 +20,19 @@ export default function SearchPage({
location: Location location: Location
}): ReactElement { }): ReactElement {
const parsed = queryString.parse(location.search) const parsed = queryString.parse(location.search)
const { text, tag } = parsed const { text, tag, page } = parsed
const [queryResult, setQueryResult] = useState<QueryResult>() const [queryResult, setQueryResult] = useState<QueryResult>()
const [loading, setLoading] = useState<boolean>()
useEffect(() => { useEffect(() => {
async function initSearch() { async function initSearch() {
setLoading(true)
const queryResult = await getResults(parsed) const queryResult = await getResults(parsed)
setQueryResult(queryResult) setQueryResult(queryResult)
setLoading(false)
} }
initSearch() initSearch()
}, [parsed]) }, [text, tag, page])
return ( return (
<section className={styles.grid}> <section className={styles.grid}>
@ -42,11 +45,7 @@ export default function SearchPage({
</aside> </aside>
<div className={styles.results}> <div className={styles.results}>
{queryResult && queryResult.results.length > 0 ? ( {loading ? <Loader /> : <AssetList queryResult={queryResult} />}
<AssetList queryResult={queryResult} />
) : (
<div className={styles.empty}>No results found.</div>
)}
</div> </div>
</section> </section>
) )

View File

@ -1,9 +1,9 @@
import { import {
SearchQuery, SearchQuery,
QueryResult QueryResult
} from '@oceanprotocol/squid/dist/node/aquarius/Aquarius' } from '@oceanprotocol/lib/dist/node/metadatastore/MetadataStore'
import { priceQueryParamToWei } from '../../../utils' import { priceQueryParamToWei } from '../../../utils'
import { Aquarius, Logger } from '@oceanprotocol/squid' import { MetadataStore, Logger } from '@oceanprotocol/lib'
import { oceanConfig } from '../../../../app.config' import { oceanConfig } from '../../../../app.config'
export function getSearchQuery( export function getSearchQuery(
@ -52,8 +52,8 @@ export async function getResults(params: any): Promise<QueryResult> {
]) ])
: undefined : undefined
const aquarius = new Aquarius(oceanConfig.aquariusUri, Logger) const metadataStore = new MetadataStore(oceanConfig.metadataStoreUri, Logger)
const queryResult = await aquarius.queryMetadata( const queryResult = await metadataStore.queryMetadata(
getSearchQuery(page, offset, text, tag, priceQuery) getSearchQuery(page, offset, text, tag, priceQuery)
) )

View File

@ -3,7 +3,6 @@ import { ToastContainer } from 'react-toastify'
import '@oceanprotocol/typographies/css/ocean-typo.css' import '@oceanprotocol/typographies/css/ocean-typo.css'
import '../global/styles.css' import '../global/styles.css'
import 'react-toastify/dist/ReactToastify.css'
export default function Styles({ export default function Styles({
children children

View File

@ -1,3 +1,5 @@
@import '../../node_modules/react-toastify/dist/ReactToastify.css';
div.Toastify__toast { div.Toastify__toast {
font-family: var(--font-family-base); font-family: var(--font-family-base);
font-size: var(--font-size-small); font-size: var(--font-size-small);

68
src/global/_web3modal.css Normal file
View File

@ -0,0 +1,68 @@
div.web3modal-modal-lightbox {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(3px);
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
animation: fadeIn 0.2s ease-out backwards;
}
div.web3modal-modal-card {
border-radius: var(--border-radius);
padding: var(--spacer);
margin: var(--spacer) auto;
max-width: var(--break-point--small);
border: 1px solid var(--brand-grey-lighter);
box-shadow: 0 6px 15px 0 rgba(0, 0, 0, 0.05);
animation: moveUp 0.2s ease-out backwards;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
div.web3modal-provider-wrapper {
border: none;
padding: 0;
}
div.web3modal-provider-container {
padding: var(--spacer);
}
div.web3modal-provider-icon {
filter: grayscale(1) contrast(150%);
transition: filter 0.2s ease-out;
}
div.web3modal-provider-wrapper:hover div.web3modal-provider-icon {
filter: none;
}
div.web3modal-provider-name {
font-size: var(--font-size-large);
font-weight: var(--font-weight-bold);
font-family: var(--font-family-title);
}
div.web3modal-provider-description {
font-size: var(--font-size-base);
margin-top: 0;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes moveUp {
from {
transform: translate3d(0, 1rem, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}

View File

@ -134,3 +134,4 @@ fieldset {
@import '_code.css'; @import '_code.css';
@import '_toast.css'; @import '_toast.css';
@import '_web3modal.css';

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { Web3Provider, OceanProvider, Config } from '@oceanprotocol/react' import { OceanProvider } from '@oceanprotocol/react'
import { oceanConfig } from '../../app.config' import { oceanConfig } from '../../app.config'
const wrapRootElement = ({ const wrapRootElement = ({
@ -7,9 +7,7 @@ const wrapRootElement = ({
}: { }: {
element: ReactElement element: ReactElement
}): ReactElement => ( }): ReactElement => (
<Web3Provider> <OceanProvider config={oceanConfig}>{element}</OceanProvider>
<OceanProvider config={oceanConfig as Config}>{element}</OceanProvider>
</Web3Provider>
) )
export default wrapRootElement export default wrapRootElement

View File

@ -2,15 +2,36 @@ import { useStaticQuery, graphql } from 'gatsby'
const query = graphql` const query = graphql`
query { query {
siteMetadata: allFile(filter: { relativePath: { eq: "site.json" } }) { site {
siteMetadata {
siteTitle
siteTagline
siteUrl
siteIcon
copyright
menu {
name
link
}
appConfig {
infuraProjectId
networks
oceanConfig {
factoryAddress
metadataStoreUri
nodeUri
providerUri
verbose
}
}
}
}
siteImage: allFile(filter: { relativePath: { eq: "site.json" } }) {
edges { edges {
node { node {
childContentJson { childContentJson {
site { site {
siteTitle
siteTagline
siteUrl
siteIcon
siteImage { siteImage {
childImageSharp { childImageSharp {
original { original {
@ -18,11 +39,6 @@ const query = graphql`
} }
} }
} }
copyright
menu {
name
link
}
} }
} }
} }
@ -33,5 +49,11 @@ const query = graphql`
export function useSiteMetadata() { export function useSiteMetadata() {
const data = useStaticQuery(query) const data = useStaticQuery(query)
return data.siteMetadata.edges[0].node.childContentJson.site
const siteMeta = {
...data.siteImage.edges[0].node.childContentJson.site,
...data.site.siteMetadata
}
return siteMeta
} }

View File

@ -4,11 +4,13 @@ import AssetContent from '../../components/organisms/AssetContent'
import Layout from '../../components/Layout' import Layout from '../../components/Layout'
import { PageProps } from 'gatsby' import { PageProps } from 'gatsby'
import { MetaDataMarket, ServiceMetaDataMarket } from '../../@types/MetaData' import { MetaDataMarket, ServiceMetaDataMarket } from '../../@types/MetaData'
import { Aquarius, Logger } from '@oceanprotocol/squid' import { MetadataStore, Logger } from '@oceanprotocol/lib'
import { oceanConfig } from '../../../app.config'
import Alert from '../../components/atoms/Alert' import Alert from '../../components/atoms/Alert'
import Loader from '../../components/atoms/Loader'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
export default function AssetRoute(props: PageProps): ReactElement { export default function AssetRoute(props: PageProps): ReactElement {
const { appConfig } = useSiteMetadata()
const [metadata, setMetadata] = useState<MetaDataMarket>() const [metadata, setMetadata] = useState<MetaDataMarket>()
const [title, setTitle] = useState<string>() const [title, setTitle] = useState<string>()
const [error, setError] = useState<string>() const [error, setError] = useState<string>()
@ -18,12 +20,15 @@ export default function AssetRoute(props: PageProps): ReactElement {
useEffect(() => { useEffect(() => {
async function init() { async function init() {
try { try {
const aquarius = new Aquarius(oceanConfig.aquariusUri, Logger) const metadataStore = new MetadataStore(
const ddo = await aquarius.retrieveDDO(did) appConfig.oceanConfig.metadataStoreUri,
Logger
)
const ddo = await metadataStore.retrieveDDO(did)
if (!ddo) { if (!ddo) {
setTitle('Could not retrieve asset') setTitle('Could not retrieve asset')
setError('The DDO was not found in Aquarius.') setError('The DDO was not found in MetadataStore.')
return return
} }
@ -39,21 +44,25 @@ export default function AssetRoute(props: PageProps): ReactElement {
} }
} }
init() init()
}, []) }, [did])
return error ? ( return did && metadata ? (
<Layout title={title} uri={props.location.pathname}>
<Router basepath="/asset">
<AssetContent
did={did}
metadata={metadata as MetaDataMarket}
path=":did"
/>
</Router>
</Layout>
) : error ? (
<Layout title={title} noPageHeader uri={props.location.pathname}> <Layout title={title} noPageHeader uri={props.location.pathname}>
<Alert title={title} text={error} state="error" /> <Alert title={title} text={error} state="error" />
</Layout> </Layout>
) : did && metadata ? (
<Layout title={title} uri={props.location.pathname}>
<Router basepath="/asset">
<AssetContent did={did} metadata={metadata} path="/asset/:did" />
</Router>
</Layout>
) : ( ) : (
<Layout title="Loading..." uri={props.location.pathname}> <Layout title="Loading..." uri={props.location.pathname}>
Loading... <Loader />
</Layout> </Layout>
) )
} }

View File

@ -1,40 +1,20 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { PageProps, graphql } from 'gatsby' import { PageProps } from 'gatsby'
import PageHome from '../components/pages/Home' import PageHome from '../components/pages/Home'
import { useSiteMetadata } from '../hooks/useSiteMetadata' import { useSiteMetadata } from '../hooks/useSiteMetadata'
import Layout from '../components/Layout' import Layout from '../components/Layout'
export default function PageGatsbyHome(props: PageProps): ReactElement { export default function PageGatsbyHome(props: PageProps): ReactElement {
const { siteTitle, siteTagline } = useSiteMetadata() const { siteTitle, siteTagline } = useSiteMetadata()
const assets = (props.data as any).allOceanAsset.edges
return ( return (
<Layout title={siteTitle} description={siteTagline} uri={props.uri}> <Layout
<PageHome assets={assets} /> title={siteTitle}
description={siteTagline}
uri={props.uri}
headerCenter
>
<PageHome />
</Layout> </Layout>
) )
} }
export const pageQuery = graphql`
query PageHomeQuery {
allOceanAsset {
edges {
node {
did
main {
type
name
dateCreated
author
price
datePublished
}
additionalInformation {
description
access
}
}
}
}
}
`

View File

@ -1,34 +0,0 @@
import axios from 'axios'
import { DID } from '@oceanprotocol/squid'
import { oceanConfig } from '../../app.config'
export declare type GetRatingResponse = {
comment: string
datePublished: string
vote: number
}
const url = oceanConfig.ratingUri + '/api/v1/rating'
export default async function getAssetRating(
did: DID | string,
account: string
): Promise<GetRatingResponse | undefined> {
try {
if (!account) return
const response = await axios.get(url, {
params: {
did: did,
address: account
}
})
const votesLength = response.data.length
if (votesLength > 0) {
return response.data[votesLength - 1]
}
} catch (error) {
console.error(error.message)
}
}

View File

@ -1,6 +1,6 @@
import axios, { AxiosResponse } from 'axios' import axios, { AxiosResponse } from 'axios'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { File } from '@oceanprotocol/squid' import { File } from '@oceanprotocol/lib'
import numeral from 'numeral' import numeral from 'numeral'
import web3Utils from 'web3-utils' import web3Utils from 'web3-utils'

View File

@ -1,49 +0,0 @@
import axios, { AxiosResponse } from 'axios'
import Web3 from 'web3'
import { DID } from '@oceanprotocol/squid'
import { oceanConfig } from '../../app.config'
export declare type RatingResponse = [string, number]
const url = oceanConfig.ratingUri + '/api/v1/rating'
export function gethash(message: string) {
let hex = ''
for (let i = 0; i < message.length; i++) {
hex += '' + message.charCodeAt(i).toString(16)
}
const hexMessage = '0x' + hex
return hexMessage
}
export default async function rateAsset(
did: DID | string,
web3: Web3,
value: number
): Promise<RatingResponse | string> {
try {
const timestamp = Math.floor(+new Date() / 1000)
const accounts = await web3.eth.getAccounts()
const signature = await web3.eth.personal.sign(
gethash(`${timestamp}`),
accounts ? accounts[0] : '',
''
)
const ratingBody = {
did,
vote: value,
comment: '',
address: accounts[0],
timestamp: timestamp,
signature: signature
}
const response: AxiosResponse = await axios.post(url, ratingBody)
if (!response) return 'No Response'
return response.data
} catch (error) {
console.error(error.message)
return `Error: ${error.message}`
}
}

55
src/utils/wallet.ts Normal file
View File

@ -0,0 +1,55 @@
import { OceanProviderValue } from '@oceanprotocol/react'
import atlas from '@ethereum-navigator/atlas'
import { networks, infuraProjectId } from '../../app.config'
const web3ModalTheme = {
background: 'var(--brand-white)',
main: 'var(--brand-black)',
secondary: 'var(--brand-grey-light)',
border: 'var(--brand-grey-lighter)',
hover: 'var(--brand-grey-dimmed)'
}
export async function connectWallet(
connect: OceanProviderValue['connect']
): Promise<void> {
const { default: WalletConnectProvider } = await import(
'@walletconnect/web3-provider'
)
const providerOptions = {
/* See Provider Options Section */
walletconnect: {
package: WalletConnectProvider, // required
options: {
infuraId: infuraProjectId // required
}
}
}
await connect({ cacheProvider: true, providerOptions, theme: web3ModalTheme })
}
export function isCorrectNetwork(chainId: number): boolean {
const allowedIds = networks
return allowedIds.includes(chainId)
}
export function accountTruncate(account: string): string {
const middle = account.substring(6, 38)
const truncated = account.replace(middle, '…')
return truncated
}
export function getNetworkName(chainId: number): string {
switch (chainId) {
case 1:
return 'Main'
case 4:
return 'Rinkeby'
case 42:
return 'Kovan'
default:
return 'Unknown'
}
}

View File

@ -1,4 +1,4 @@
import { DDO } from '@oceanprotocol/squid' import { DDO } from '@oceanprotocol/lib'
import { MetaDataMarket } from '../../../src/@types/MetaData' import { MetaDataMarket } from '../../../src/@types/MetaData'
const ddo: Partial<DDO> = { const ddo: Partial<DDO> = {

View File

@ -1,4 +1,4 @@
import { ComputeJob } from '@oceanprotocol/squid' import { ComputeJob } from '@oceanprotocol/lib'
// ComputeJob need to be updated in squid // ComputeJob need to be updated in squid
const job: Partial<ComputeJob> = { const job: Partial<ComputeJob> = {

View File

@ -1,7 +1,7 @@
import ddo from '../../__fixtures__/ddo' import ddo from '../../__fixtures__/ddo'
import job from '../../__fixtures__/job' import job from '../../__fixtures__/job'
const aquarius = { const metadataStore = {
queryMetadata: () => { queryMetadata: () => {
return { return {
results: [] as any[], results: [] as any[],
@ -12,13 +12,13 @@ const aquarius = {
} }
const squidMock = { const squidMock = {
Aquarius: () => aquarius, MetadataStore: () => metadataStore,
DDO: () => ddo, DDO: () => ddo,
ocean: { ocean: {
accounts: { accounts: {
list: () => ['xxx', 'xxx'] list: () => ['xxx', 'xxx']
}, },
aquarius, metadataStore,
compute: { compute: {
status: (account: string) => { status: (account: string) => {
return [job] return [job]
@ -69,13 +69,13 @@ const squidMock = {
name: 'Squid-js', name: 'Squid-js',
status: 'Working' status: 'Working'
}, },
aquarius: { metadataStore: {
name: 'Aquarius', name: 'MetadataStore',
status: 'Working' status: 'Working'
}, },
brizo: { provider: {
name: 'Brizo', name: 'Provider',
network: 'Nile', network: 'Rinkeby',
status: 'Working', status: 'Working',
contracts: { contracts: {
hello: 'hello', hello: 'hello',

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import squidMock from './squid' import libMock from './lib'
import web3ProviderMock from '../web3provider' import web3ProviderMock from '../web3provider'
const reactMock = { const reactMock = {
@ -19,7 +19,7 @@ const reactMock = {
}, },
useOcean: () => { useOcean: () => {
return { return {
ocean: squidMock.ocean ocean: libMock.ocean
} }
}, },
useWeb3: () => { useWeb3: () => {

View File

@ -1,10 +0,0 @@
import React from 'react'
import { render } from '@testing-library/react'
import { Web3Provider } from '@oceanprotocol/react'
describe('Web3Provider', () => {
it('renders without crashing', () => {
const { container } = render(<Web3Provider>Children</Web3Provider>)
expect(container).toHaveTextContent('Children')
})
})

View File

@ -1,23 +0,0 @@
import axios, { AxiosResponse } from 'axios'
import getAssetRating, {
GetRatingResponse
} from '../../../src/utils/getAssetRating'
jest.mock('axios')
describe('getAssetRating()', () => {
it('success', async () => {
const ratingResponse: GetRatingResponse = {
comment: '',
datePublished: '',
vote: 5
}
;(axios.get as any).mockResolvedValueOnce({
data: [ratingResponse, ratingResponse]
} as AxiosResponse)
const response = await getAssetRating('0x00', '0x00')
expect(response && response.vote).toBe(5)
})
})

View File

@ -1,38 +0,0 @@
import axios, { AxiosResponse } from 'axios'
import rateAsset, { RatingResponse } from '../../../src/utils/rateAsset'
import web3 from '../__mocks__/web3'
jest.mock('axios')
describe('rateAsset()', () => {
it('success', async () => {
;(axios.post as any).mockResolvedValueOnce({
data: ['4.0', 1]
} as AxiosResponse)
const response: RatingResponse | string = await rateAsset('0x00', web3, 5)
expect(response && response[0]).toBe('4.0')
})
it('string return', async () => {
;(axios.post as any).mockResolvedValueOnce({
data: 'Missing signature'
} as AxiosResponse)
const response: RatingResponse | string = await rateAsset('0x00', web3, 5)
expect(response && response).toBe('Missing signature')
})
it('error catch', async () => {
;(axios.post as any).mockResolvedValueOnce({
data: {}
} as AxiosResponse)
const response: RatingResponse | string = await rateAsset(
'0x00',
{} as any,
5
)
expect(response && response).toContain('Error: ')
})
})