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:
commit
74e2f95444
37
.env.example
37
.env.example
@ -1,26 +1,15 @@
|
||||
#Spree config
|
||||
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'
|
||||
GATSBY_INFURA_PROJECT_ID="xxx"
|
||||
|
||||
#Nile market
|
||||
#NODE_URI='https://nile.dev-ocean.com'
|
||||
#AQUARIUS_URI='https://aquarius.nile.market.dev-ocean.com'
|
||||
#BRIZO_URI='https://brizo.nile.market.dev-ocean.com'
|
||||
#BRIZO_ADDRESS='0xeD792C5FcC8bF3322a6ba89A6e51eF0B6fB3C530'
|
||||
#SECRET_STORE_URI='https://secret-store.nile.dev-ocean.com'
|
||||
#FAUCET_URI='https://faucet.nile.dev-ocean.com'
|
||||
#RATING_URI='https://rating.nile.market.dev-ocean.com'
|
||||
# Local config
|
||||
GATSBY_NODE_URI='http://localhost:8545'
|
||||
GATSBY_METADATA_STORE_URI='http://aquarius:5000'
|
||||
GATSBY_PROVIDER_URI='http://localhost:8030'
|
||||
GATSBY_FACTORY_ADDRESS='0xxxx'
|
||||
#GATSBY_OCEAN_TOKEN_ADDRESS='0xxxx'
|
||||
|
||||
#Pacific market
|
||||
#NODE_URI='https://pacific.oceanprotocol.com'
|
||||
#AQUARIUS_URI='https://aquarius.pacific.market.dev-ocean.com'
|
||||
#BRIZO_URI='https://brizo.pacific.market.dev-ocean.com'
|
||||
#BRIZO_ADDRESS='0xeD792C5FcC8bF3322a6ba89A6e51eF0B6fB3C530'
|
||||
#SECRET_STORE_URI='https://secret-store.oceanprotocol.com'
|
||||
#FAUCET_URI='https://faucet.oceanprotocol.com'
|
||||
#RATING_URI='https://rating.pacific.market.dev-ocean.com'
|
||||
# Rinkeby
|
||||
#GATSBY_NODE_URI='https://rinkeby.infura.io/v3/GATSBY_INFURA_PROJECT_ID'
|
||||
#GATSBY_METADATA_STORE_URI='https://aquarius.rinkeby.v3.dev-ocean.com'
|
||||
#GATSBY_PROVIDER_URI='https://provider.rinkeby.v3.dev-ocean.com'
|
||||
#GATSBY_FACTORY_ADDRESS='0xB9d406D24B310A7D821D0b782a36909e8c925471'
|
||||
#GATSBY_OCEAN_TOKEN_ADDRESS='0xf6AE724aD6e6Fa89B6aBc9710C5eb692b7F57139'
|
||||
|
11
README.md
11
README.md
@ -174,13 +174,12 @@ vercel alias
|
||||
|
||||
## 🏗 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.nile.market.dev-ocean.com`
|
||||
- `brizo.nile.market.dev-ocean.com`
|
||||
- `[aquarius.rinkeby.v3.dev-ocean.com](https://aquarius.rinkeby.v3.dev-ocean.com)`
|
||||
- `[provider.rinkeby.v3.dev-ocean.com](https://provider.rinkeby.v3.dev-ocean.com)`
|
||||
|
||||
Edit command with `kubectl`, e.g.:
|
||||
|
||||
@ -188,7 +187,7 @@ Edit command with `kubectl`, e.g.:
|
||||
kubectl edit deployment -n market-nile aquarius
|
||||
```
|
||||
|
||||
**Pacific (Production)**
|
||||
**Main (Production)**
|
||||
|
||||
- K8 namespace: `market-pacific`
|
||||
- `aquarius.pacific.market.dev-ocean.com`
|
||||
|
@ -1,19 +1,24 @@
|
||||
module.exports = {
|
||||
oceanConfig: {
|
||||
nodeUri: process.env.NODE_URI || 'https://pacific.oceanprotocol.com',
|
||||
aquariusUri:
|
||||
process.env.AQUARIUS_URI ||
|
||||
'https://aquarius.marketplace.oceanprotocol.com',
|
||||
brizoUri:
|
||||
process.env.BRIZO_URI || 'https://brizo.marketplace.oceanprotocol.com',
|
||||
brizoAddress:
|
||||
process.env.BRIZO_ADDRESS || '0x00c6A0BC5cD0078d6Cd0b659E8061B404cfa5704',
|
||||
secretStoreUri:
|
||||
process.env.SECRET_STORE_URI || 'https://secret-store.oceanprotocol.com',
|
||||
faucetUri: process.env.FAUCET_URI || 'https://faucet.oceanprotocol.com',
|
||||
ratingUri:
|
||||
process.env.RATING_URI ||
|
||||
'https://rating.pacific.marketplace.dev-ocean.com',
|
||||
nodeUri:
|
||||
process.env.GATSBY_NODE_URI ||
|
||||
`https://rinkeby.infura.io/${process.env.GATSBY_INFURA_PROJECT_ID}`,
|
||||
metadataStoreUri:
|
||||
process.env.GATSBY_METADATA_STORE_URI ||
|
||||
'https://aquarius.rinkeby.v3.dev-ocean.com',
|
||||
providerUri:
|
||||
process.env.GATSBY_PROVIDER_URI ||
|
||||
'https://provider.rinkeby.v3.dev-ocean.com',
|
||||
factoryAddress:
|
||||
process.env.GATSBY_FACTORY_ADDRESS ||
|
||||
'0xB9d406D24B310A7D821D0b782a36909e8c925471',
|
||||
oceanTokenAddress:
|
||||
process.env.GATSBY_OCEAN_TOKEN_ADDRESS ||
|
||||
'0xf6AE724aD6e6Fa89B6aBc9710C5eb692b7F57139',
|
||||
verbose: 3
|
||||
}
|
||||
},
|
||||
// Main, Rinkeby, Kovan
|
||||
// networks: [1, 4, 42],
|
||||
networks: [4],
|
||||
infuraProjectId: process.env.GATSBY_INFURA_PROJECT_ID || 'xxx'
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"site": {
|
||||
"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",
|
||||
"siteIcon": "node_modules/@oceanprotocol/art/logo/favicon-white.png",
|
||||
"siteImage": "../src/images/share.png",
|
||||
|
@ -1,11 +1,14 @@
|
||||
require('dotenv').config()
|
||||
|
||||
const siteContent = require('./content/site.json')
|
||||
const { oceanConfig } = require('./app.config')
|
||||
const appConfig = require('./app.config')
|
||||
|
||||
module.exports = {
|
||||
siteMetadata: {
|
||||
...siteContent.site
|
||||
...siteContent.site,
|
||||
appConfig: {
|
||||
...appConfig
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
@ -29,12 +32,6 @@ module.exports = {
|
||||
path: `${__dirname}/node_modules/@oceanprotocol/art/`
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-source-ocean',
|
||||
options: {
|
||||
aquariusUri: oceanConfig.aquariusUri
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-plugin-sharp',
|
||||
options: {
|
||||
|
@ -1,5 +1,3 @@
|
||||
const path = require('path')
|
||||
|
||||
exports.onCreateWebpackConfig = ({ actions }) => {
|
||||
actions.setWebpackConfig({
|
||||
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 }) => {
|
||||
const { createPage } = actions
|
||||
// page.matchPath is a special key that's used for matching pages
|
||||
|
1106
package-lock.json
generated
1106
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@ -22,20 +22,21 @@
|
||||
"@loadable/component": "^5.13.1",
|
||||
"@now/node": "^1.7.2",
|
||||
"@oceanprotocol/art": "^3.0.0",
|
||||
"@oceanprotocol/react": "0.0.11",
|
||||
"@oceanprotocol/squid": "^2.2.0",
|
||||
"@oceanprotocol/lib": "^0.1.4",
|
||||
"@oceanprotocol/react": "^0.0.12",
|
||||
"@oceanprotocol/typographies": "^0.1.0",
|
||||
"@sindresorhus/slugify": "^1.0.0",
|
||||
"@tippyjs/react": "^4.1.0",
|
||||
"@types/classnames": "^2.2.10",
|
||||
"@walletconnect/web3-provider": "^1.0.15",
|
||||
"axios": "^0.19.2",
|
||||
"classnames": "^2.2.6",
|
||||
"date-fns": "^2.14.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"ethereum-blockies": "github:MyEtherWallet/blockies",
|
||||
"filesize": "^6.1.0",
|
||||
"formik": "^2.1.4",
|
||||
"gatsby": "^2.24.2",
|
||||
"formik": "^2.1.5",
|
||||
"gatsby": "^2.24.3",
|
||||
"gatsby-image": "^2.4.13",
|
||||
"gatsby-plugin-manifest": "^2.4.18",
|
||||
"gatsby-plugin-react-helmet": "^3.3.10",
|
||||
@ -53,15 +54,14 @@
|
||||
"numeral": "^2.0.6",
|
||||
"query-string": "^6.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-dom": "^16.13.1",
|
||||
"react-dotdotdot": "^1.3.1",
|
||||
"react-dropzone": "^11.0.1",
|
||||
"react-dropzone": "^11.0.2",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-paginate": "^6.3.2",
|
||||
"react-rating": "^2.0.5",
|
||||
"react-responsive-modal": "^5.0.2",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-tabs": "^3.1.1",
|
||||
@ -73,17 +73,17 @@
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.3",
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/preset-typescript": "^7.10.1",
|
||||
"@storybook/addon-actions": "^6.0.0-rc.3",
|
||||
"@storybook/addon-storyshots": "^6.0.0-rc.3",
|
||||
"@storybook/react": "^6.0.0-rc.3",
|
||||
"@storybook/addon-actions": "^6.0.0-rc.5",
|
||||
"@storybook/addon-storyshots": "^6.0.0-rc.5",
|
||||
"@storybook/react": "^6.0.0-rc.5",
|
||||
"@svgr/webpack": "^5.4.0",
|
||||
"@testing-library/jest-dom": "^5.11.0",
|
||||
"@testing-library/react": "^10.4.5",
|
||||
"@testing-library/jest-dom": "^5.11.1",
|
||||
"@testing-library/react": "^10.4.7",
|
||||
"@types/jest": "^26.0.4",
|
||||
"@types/loadable__component": "^5.13.0",
|
||||
"@types/node": "^14.0.22",
|
||||
"@types/node": "^14.0.23",
|
||||
"@types/numeral": "^0.0.28",
|
||||
"@types/react": "^16.9.43",
|
||||
"@types/react-datepicker": "^3.0.2",
|
||||
@ -92,8 +92,8 @@
|
||||
"@types/react-tabs": "^2.3.2",
|
||||
"@types/shortid": "0.0.29",
|
||||
"@types/yup": "^0.29.3",
|
||||
"@typescript-eslint/eslint-plugin": "^3.6.0",
|
||||
"@typescript-eslint/parser": "^3.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.6.1",
|
||||
"@typescript-eslint/parser": "^3.6.1",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-preset-react-app": "^9.1.2",
|
||||
"electron": "^9.1.0",
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"name": "gatsby-source-ocean"
|
||||
}
|
9
src/@types/MetaData.d.ts
vendored
9
src/@types/MetaData.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
import { File, MetaData, AdditionalInformation } from '@oceanprotocol/squid'
|
||||
import { ServiceMetadata } from '@oceanprotocol/squid/dist/node/ddo/Service'
|
||||
import { File, MetaData, AdditionalInformation } from '@oceanprotocol/lib'
|
||||
import { ServiceMetadata } from '@oceanprotocol/lib/dist/node/ddo/Service'
|
||||
|
||||
export declare type AccessType = 'Download' | 'Compute'
|
||||
|
||||
@ -34,8 +34,3 @@ export interface MetaDataPublishForm {
|
||||
export interface ServiceMetaDataMarket extends ServiceMetadata {
|
||||
attributes: MetaDataMarket
|
||||
}
|
||||
|
||||
// type for assets pulled into GraphQL
|
||||
export interface OceanAsset extends MetaDataMarket {
|
||||
did: DID
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
.app {
|
||||
height: 100%;
|
||||
background: url('../../node_modules/@oceanprotocol/art/waves/waves.svg')
|
||||
no-repeat center 10rem;
|
||||
|
||||
/* sticky footer technique */
|
||||
display: flex;
|
||||
|
@ -12,6 +12,7 @@ export interface LayoutProps {
|
||||
uri: string
|
||||
description?: string
|
||||
noPageHeader?: boolean
|
||||
headerCenter?: boolean
|
||||
}
|
||||
|
||||
export default function Layout({
|
||||
@ -19,7 +20,8 @@ export default function Layout({
|
||||
title,
|
||||
uri,
|
||||
description,
|
||||
noPageHeader
|
||||
noPageHeader,
|
||||
headerCenter
|
||||
}: LayoutProps): ReactElement {
|
||||
return (
|
||||
<div className={styles.app}>
|
||||
@ -29,7 +31,11 @@ export default function Layout({
|
||||
<main className={styles.main}>
|
||||
<Container>
|
||||
{title && !noPageHeader && (
|
||||
<PageHeader title={title} description={description} />
|
||||
<PageHeader
|
||||
title={title}
|
||||
description={description}
|
||||
center={headerCenter}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</Container>
|
||||
|
@ -1,6 +1,9 @@
|
||||
import React, { ReactElement, ReactNode } from 'react'
|
||||
import classNames from 'classnames/bind'
|
||||
import styles from './Container.module.css'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
||||
export default function Container({
|
||||
children,
|
||||
narrow,
|
||||
@ -10,13 +13,11 @@ export default function Container({
|
||||
narrow?: boolean
|
||||
className?: string
|
||||
}): ReactElement {
|
||||
return (
|
||||
<div
|
||||
className={`${styles.container} ${narrow && styles.narrow} ${
|
||||
className && className
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
const styleClasses = cx({
|
||||
container: true,
|
||||
narrow: narrow,
|
||||
[className]: className
|
||||
})
|
||||
|
||||
return <div className={styleClasses}>{children}</div>
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import { File as FileMetaData } from '@oceanprotocol/squid'
|
||||
import { File as FileMetaData } from '@oceanprotocol/lib'
|
||||
import filesize from 'filesize'
|
||||
import cleanupContentType from '../../utils/cleanupContentType'
|
||||
import styles from './File.module.css'
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
}}
|
||||
/>
|
||||
)
|
@ -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>
|
||||
)
|
||||
}
|
@ -7,14 +7,13 @@
|
||||
.tag {
|
||||
color: var(--color-secondary);
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: var(--font-weight-bold);
|
||||
padding: 0.2rem 1.2rem 0.2rem 1.2rem;
|
||||
margin-left: calc(var(--spacer) / 16);
|
||||
margin-right: calc(var(--spacer) / 16);
|
||||
margin-bottom: calc(var(--spacer) / 8);
|
||||
padding: 0.1rem 1.2rem 0.1rem 1.2rem;
|
||||
margin-left: calc(var(--spacer) / 8);
|
||||
margin-right: calc(var(--spacer) / 8);
|
||||
margin-bottom: calc(var(--spacer) / 4);
|
||||
text-align: center;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--brand-grey-light);
|
||||
border: 1px solid var(--brand-grey-lighter);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@ -25,10 +24,6 @@
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.tag:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.more {
|
||||
font-size: var(--font-size-mini);
|
||||
margin-left: calc(var(--spacer) / 8);
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
.foot {
|
||||
font-weight: var(--font-weight-bold);
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
}
|
||||
|
||||
.foot p {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import AssetTeaser from '../molecules/AssetTeaser'
|
||||
import * as React from 'react'
|
||||
import { DDO } from '@oceanprotocol/squid'
|
||||
import { DDO } from '@oceanprotocol/lib'
|
||||
import ddo from '../../../tests/unit/__fixtures__/ddo'
|
||||
|
||||
export default {
|
||||
|
@ -2,10 +2,8 @@ import React from 'react'
|
||||
import { Link } from 'gatsby'
|
||||
import Dotdotdot from 'react-dotdotdot'
|
||||
import { MetaDataMarket } from '../../@types/MetaData'
|
||||
import Tags from '../atoms/Tags'
|
||||
import Price from '../atoms/Price'
|
||||
import styles from './AssetTeaser.module.css'
|
||||
import Rating from '../atoms/Rating'
|
||||
|
||||
declare type AssetTeaserProps = {
|
||||
did: string
|
||||
@ -16,17 +14,10 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
||||
did,
|
||||
metadata
|
||||
}: AssetTeaserProps) => {
|
||||
if (!metadata.additionalInformation) return null
|
||||
|
||||
const { name, price } = metadata.main
|
||||
|
||||
const {
|
||||
description,
|
||||
copyrightHolder,
|
||||
tags,
|
||||
categories,
|
||||
access
|
||||
} = metadata.additionalInformation
|
||||
|
||||
const { curation } = metadata
|
||||
const { description, access } = metadata.additionalInformation
|
||||
|
||||
return (
|
||||
<article className={styles.teaser}>
|
||||
@ -35,20 +26,15 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
||||
{access === 'Compute' && (
|
||||
<div className={styles.accessLabel}>{access}</div>
|
||||
)}
|
||||
<Rating curation={curation} readonly />
|
||||
|
||||
<div className={styles.content}>
|
||||
<Dotdotdot tagName="p" clamp={3}>
|
||||
{description || ''}
|
||||
</Dotdotdot>
|
||||
|
||||
{tags && (
|
||||
<Tags className={styles.tags} items={tags} max={3} noLinks />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<footer className={styles.foot}>
|
||||
<Price price={price} small />
|
||||
<Price price={price} />
|
||||
</footer>
|
||||
</Link>
|
||||
</article>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useNavigate } from '@reach/router'
|
||||
import { DDO } from '@oceanprotocol/squid'
|
||||
import { DDO } from '@oceanprotocol/lib'
|
||||
import { redeploy } from '../../utils'
|
||||
import Button from '../atoms/Button'
|
||||
import BaseDialog from '../atoms/BaseDialog'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import { File } from '@oceanprotocol/squid'
|
||||
import { File } from '@oceanprotocol/lib'
|
||||
import { prettySize } from '../../../utils'
|
||||
import cleanupContentType from '../../../utils/cleanupContentType'
|
||||
import styles from './Info.module.css'
|
||||
|
@ -14,3 +14,9 @@
|
||||
margin-top: calc(var(--spacer) / 4);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -6,14 +6,16 @@ const cx = classNames.bind(styles)
|
||||
|
||||
export default function PageHeader({
|
||||
title,
|
||||
description
|
||||
description,
|
||||
center
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
center?: boolean
|
||||
}): ReactElement {
|
||||
const styleClasses = cx({
|
||||
header: true
|
||||
header: true,
|
||||
center: center
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -15,7 +15,7 @@
|
||||
margin-top: -1px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--brand-grey-light);
|
||||
border: 1px solid var(--brand-grey-lighter);
|
||||
min-width: 3.5rem;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect, ReactElement } from 'react'
|
||||
import ReactPaginate from 'react-paginate'
|
||||
import styles from './Pagination.module.css'
|
||||
|
||||
@ -14,7 +14,9 @@ export default function Pagination({
|
||||
currentPage,
|
||||
hrefBuilder,
|
||||
onPageChange
|
||||
}: PaginationProps) {
|
||||
}: PaginationProps): ReactElement {
|
||||
if (!totalPages || totalPages < 2) return null
|
||||
|
||||
const [smallViewport, setSmallViewport] = useState(true)
|
||||
|
||||
function viewportChange(mq: { matches: boolean }) {
|
||||
@ -31,7 +33,7 @@ export default function Pagination({
|
||||
}
|
||||
}, [])
|
||||
|
||||
return totalPages > 1 ? (
|
||||
return (
|
||||
<ReactPaginate
|
||||
pageCount={totalPages}
|
||||
// react-pagination starts counting at 0, we start at 1
|
||||
@ -53,5 +55,5 @@ export default function Pagination({
|
||||
disabledClassName={styles.prevNextDisabled}
|
||||
breakLinkClassName={styles.break}
|
||||
/>
|
||||
) : null
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useState, ChangeEvent, FormEvent, ReactElement } from 'react'
|
||||
import { useNavigate } from '@reach/router'
|
||||
import styles from './SearchBar.module.css'
|
||||
import Loader from '../atoms/Loader'
|
||||
import Button from '../atoms/Button'
|
||||
import Input from '../atoms/Input'
|
||||
import InputGroup from '../atoms/Input/InputGroup'
|
||||
@ -19,7 +18,6 @@ export default function SearchBar({
|
||||
}): ReactElement {
|
||||
const navigate = useNavigate()
|
||||
const [value, setValue] = useState(initialValue || '')
|
||||
const [searchStarted, setSearchStarted] = useState(false)
|
||||
|
||||
function handleChange(e: ChangeEvent<HTMLInputElement>) {
|
||||
setValue(e.target.value)
|
||||
@ -27,10 +25,7 @@ export default function SearchBar({
|
||||
|
||||
function startSearch(e: FormEvent<HTMLButtonElement>) {
|
||||
e.preventDefault()
|
||||
|
||||
if (value === '') return
|
||||
|
||||
setSearchStarted(true)
|
||||
navigate(`/search?text=${value}`)
|
||||
}
|
||||
|
||||
@ -46,7 +41,7 @@ export default function SearchBar({
|
||||
required
|
||||
/>
|
||||
<Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}>
|
||||
{searchStarted ? <Loader /> : 'Search'}
|
||||
Search
|
||||
</Button>
|
||||
</InputGroup>
|
||||
|
||||
|
@ -1,15 +1,14 @@
|
||||
import React from 'react'
|
||||
import styles from './Account.module.css'
|
||||
import { useWeb3, useOcean } from '@oceanprotocol/react'
|
||||
import { useOcean } from '@oceanprotocol/react'
|
||||
import { toDataUrl } from 'ethereum-blockies'
|
||||
import { ReactComponent as Caret } from '../../../images/caret.svg'
|
||||
import Status from '../../atoms/Status'
|
||||
|
||||
function accountTruncate(account: string) {
|
||||
const middle = account.substring(6, 38)
|
||||
const truncated = account.replace(middle, '…')
|
||||
return truncated
|
||||
}
|
||||
import {
|
||||
accountTruncate,
|
||||
connectWallet,
|
||||
isCorrectNetwork
|
||||
} from '../../../utils/wallet'
|
||||
|
||||
const Blockies = ({ account }: { account: string | undefined }) => {
|
||||
if (!account) return null
|
||||
@ -28,15 +27,14 @@ const Blockies = ({ account }: { account: string | undefined }) => {
|
||||
// Forward ref for Tippy.js
|
||||
// eslint-disable-next-line
|
||||
const Account = React.forwardRef((props, ref: any) => {
|
||||
const { account, web3Connect, ethProviderStatus } = useWeb3()
|
||||
const { status } = useOcean()
|
||||
const hasSuccess = ethProviderStatus === 1 && status === 1
|
||||
const { accountId, status, connect, chainId } = useOcean()
|
||||
const hasSuccess = status === 1 && isCorrectNetwork(chainId)
|
||||
|
||||
return account ? (
|
||||
return accountId ? (
|
||||
<button className={styles.button} aria-label="Account" ref={ref}>
|
||||
<Blockies account={account} />
|
||||
<span className={styles.address} title={account}>
|
||||
{accountTruncate(account)}
|
||||
<Blockies account={accountId} />
|
||||
<span className={styles.address} title={accountId}>
|
||||
{accountTruncate(accountId)}
|
||||
</span>
|
||||
{!hasSuccess && (
|
||||
<Status className={styles.status} state="warning" aria-hidden />
|
||||
@ -46,7 +44,7 @@ const Account = React.forwardRef((props, ref: any) => {
|
||||
) : (
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={() => web3Connect.connect()}
|
||||
onClick={async () => await connectWallet(connect)}
|
||||
// Need the `ref` here although we do not want
|
||||
// the Tippy to show in this state.
|
||||
ref={ref}
|
||||
|
@ -5,21 +5,31 @@
|
||||
max-width: 25rem;
|
||||
}
|
||||
|
||||
.details > ul {
|
||||
margin-bottom: calc(var(--spacer) / 4);
|
||||
}
|
||||
|
||||
.balance {
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-secondary);
|
||||
white-space: nowrap;
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
|
||||
.balance span {
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-bold);
|
||||
width: 20%;
|
||||
text-align: right;
|
||||
font-weight: var(--font-weight-base);
|
||||
font-size: var(--font-size-small);
|
||||
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,
|
||||
|
@ -1,30 +1,49 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import Button from '../../atoms/Button'
|
||||
import styles from './Details.module.css'
|
||||
import { useWeb3, useOcean } from '@oceanprotocol/react'
|
||||
import { useOcean } from '@oceanprotocol/react'
|
||||
import Web3Feedback from './Feedback'
|
||||
import { formatNumber } from '../../../utils'
|
||||
import { connectWallet, getNetworkName } from '../../../utils/wallet'
|
||||
import { getInjectedProviderName } from 'web3modal'
|
||||
|
||||
export default function Details({ attrs }: { attrs: any }): ReactElement {
|
||||
const { balance, web3Connect } = useWeb3()
|
||||
const { balanceInOcean } = useOcean()
|
||||
const ethBalanceText = formatNumber(Number(balance))
|
||||
const oceanBalanceText = formatNumber(Number(balanceInOcean))
|
||||
const { ocean, balance, connect, logout, chainId } = useOcean()
|
||||
const [balanceOcean, setBalanceOcean] = useState('0')
|
||||
|
||||
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 (
|
||||
<div className={styles.details} {...attrs}>
|
||||
<ul>
|
||||
<li className={styles.balance}>
|
||||
OCEAN <span>{oceanBalanceText}</span>
|
||||
<span>OCEAN</span> {balanceOcean}
|
||||
</li>
|
||||
<li className={styles.balance}>
|
||||
ETH <span>{ethBalanceText}</span>
|
||||
<span>ETH</span> {formatNumber(Number(balance))}
|
||||
</li>
|
||||
<li>
|
||||
<li className={styles.actions}>
|
||||
<span title="Connected provider">
|
||||
{getInjectedProviderName()}
|
||||
<br />
|
||||
{getNetworkName(chainId)}
|
||||
</span>
|
||||
<Button
|
||||
style="text"
|
||||
size="small"
|
||||
onClick={() => web3Connect.toggleModal()}
|
||||
onClick={() => {
|
||||
logout()
|
||||
connectWallet(connect)
|
||||
}}
|
||||
>
|
||||
Switch Wallet
|
||||
</Button>
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import Status from '../../atoms/Status'
|
||||
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 = {
|
||||
status: 'error' | 'warning' | 'success'
|
||||
@ -14,47 +16,46 @@ export default function Web3Feedback({
|
||||
}: {
|
||||
isBalanceInsufficient?: boolean
|
||||
}): ReactElement {
|
||||
const { ethProviderStatus } = useWeb3()
|
||||
const { status } = useOcean()
|
||||
const isEthProviderAbsent = ethProviderStatus === -1
|
||||
const isEthProviderDisconnected = ethProviderStatus === 0
|
||||
const isOceanDisconnected = status === 0
|
||||
const { appConfig } = useSiteMetadata()
|
||||
const { account, status, chainId } = useOcean()
|
||||
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'
|
||||
: hasSuccess && !isBalanceInsufficient
|
||||
: !correctNetwork
|
||||
? 'warning'
|
||||
: account && !isBalanceInsufficient
|
||||
? 'success'
|
||||
: 'warning'
|
||||
|
||||
const title = isEthProviderAbsent
|
||||
? 'No Web3 Browser'
|
||||
: isEthProviderDisconnected
|
||||
const title = !account
|
||||
? 'No account connected'
|
||||
: isOceanDisconnected
|
||||
? 'Not connected to Pacific network'
|
||||
: isOceanConnectionError
|
||||
? 'Error connecting to Ocean'
|
||||
: hasSuccess
|
||||
: !correctNetwork
|
||||
? 'Wrong Network'
|
||||
: account
|
||||
? isBalanceInsufficient === true
|
||||
? 'Insufficient balance'
|
||||
: 'Connected to Ocean'
|
||||
: 'Something went wrong'
|
||||
|
||||
const message = isEthProviderAbsent
|
||||
? 'To download data sets you need a browser with Web3 capabilties, like Firefox with MetaMask installed.'
|
||||
: isEthProviderDisconnected
|
||||
const message = !account
|
||||
? 'Please connect your Web3 wallet.'
|
||||
: isOceanDisconnected
|
||||
? 'Please connect in MetaMask to custom RPC https://pacific.oceanprotocol.com.'
|
||||
: isOceanConnectionError
|
||||
? 'Try again.'
|
||||
? 'Please try again.'
|
||||
: !correctNetwork
|
||||
? `Please connect to ${allowedNetworkNames}.`
|
||||
: isBalanceInsufficient === true
|
||||
? 'You do not have enough OCEAN in your wallet to purchase this asset.'
|
||||
: 'Something went wrong.'
|
||||
|
||||
return !hasSuccess ? (
|
||||
return showFeedback ? (
|
||||
<section className={styles.feedback}>
|
||||
<Status state={state} aria-hidden />
|
||||
<h3 className={styles.title}>{title}</h3>
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import loadable from '@loadable/component'
|
||||
import { useSpring, animated } from 'react-spring'
|
||||
import { useWeb3 } from '@oceanprotocol/react'
|
||||
import Account from './Account'
|
||||
import Details from './Details'
|
||||
import styles from './index.module.css'
|
||||
import { useOcean } from '@oceanprotocol/react'
|
||||
|
||||
const Tippy = loadable(() => import('@tippyjs/react/headless'))
|
||||
|
||||
@ -15,7 +15,7 @@ const animation = {
|
||||
}
|
||||
|
||||
export default function Wallet(): ReactElement {
|
||||
const { account, ethProviderStatus } = useWeb3()
|
||||
const { accountId } = useOcean()
|
||||
const [props, setSpring] = useSpring(() => animation.from)
|
||||
|
||||
function onMount() {
|
||||
@ -34,14 +34,12 @@ export default function Wallet(): ReactElement {
|
||||
})
|
||||
}
|
||||
|
||||
const isEthProviderAbsent = ethProviderStatus === -1
|
||||
if (isEthProviderAbsent) return null
|
||||
|
||||
return (
|
||||
<Tippy
|
||||
interactive
|
||||
interactiveBorder={30}
|
||||
trigger="click focus"
|
||||
zIndex={1}
|
||||
render={(attrs: any) => (
|
||||
<animated.div style={props}>
|
||||
<Details attrs={attrs} />
|
||||
@ -53,7 +51,7 @@ export default function Wallet(): ReactElement {
|
||||
animation
|
||||
onMount={onMount}
|
||||
onHide={onHide}
|
||||
disabled={!account}
|
||||
disabled={!accountId}
|
||||
fallback={<Account />}
|
||||
>
|
||||
<Account />
|
||||
|
@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
|
||||
import Compute from './Compute'
|
||||
import ddo from '../../../../tests/unit/__fixtures__/ddo'
|
||||
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'
|
||||
|
||||
export default {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, ReactElement } from 'react'
|
||||
import { Ocean } from '@oceanprotocol/squid'
|
||||
import { Ocean } from '@oceanprotocol/lib'
|
||||
import { fromWei } from 'web3-utils'
|
||||
import compareAsBN, { Comparisson } from '../../../utils/compareAsBN'
|
||||
import Loader from '../../atoms/Loader'
|
||||
|
@ -13,7 +13,7 @@ export default function AssetActions({
|
||||
metadata: MetaDataMarket
|
||||
did: string
|
||||
}): ReactElement {
|
||||
const { ocean, balanceInOcean } = useOcean()
|
||||
// const { ocean, balanceInOcean } = useOcean()
|
||||
const { access } = metadata.additionalInformation
|
||||
const isCompute = access && access === 'Compute'
|
||||
|
||||
@ -26,12 +26,13 @@ export default function AssetActions({
|
||||
<div className={styles.tabContent}>
|
||||
<TabPanel>
|
||||
{isCompute ? (
|
||||
<Compute
|
||||
did={did}
|
||||
metadata={metadata}
|
||||
ocean={ocean}
|
||||
balance={balanceInOcean}
|
||||
/>
|
||||
// <Compute
|
||||
// did={did}
|
||||
// metadata={metadata}
|
||||
// ocean={ocean}
|
||||
// balance={balanceInOcean}
|
||||
// />
|
||||
'Compute Me'
|
||||
) : (
|
||||
<Consume did={did} metadata={metadata} />
|
||||
)}
|
||||
|
@ -1,9 +1,6 @@
|
||||
.metaFull {
|
||||
margin-top: calc(var(--spacer) * 2);
|
||||
font-size: var(--font-size-small);
|
||||
padding: var(--spacer);
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--brand-grey-dimmed);
|
||||
display: grid;
|
||||
gap: var(--spacer);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
@ -12,35 +12,14 @@ export default function MetaFull({
|
||||
metadata: MetaDataMarket
|
||||
}): ReactElement {
|
||||
const { dateCreated, datePublished, author, license } = metadata.main
|
||||
let dateRange
|
||||
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]
|
||||
}
|
||||
const { categories } = metadata.additionalInformation
|
||||
|
||||
return (
|
||||
<div className={styles.metaFull}>
|
||||
<MetaItem title="Author" content={author} />
|
||||
<MetaItem title="License" content={license} />
|
||||
<MetaItem
|
||||
title="Data Created"
|
||||
content={
|
||||
dateRange && dateRange[0] !== dateRange[1] ? (
|
||||
<>
|
||||
<Time date={dateRange[0]} />
|
||||
{' – '}
|
||||
<Time date={dateRange[1]} />
|
||||
</>
|
||||
) : (
|
||||
<Time date={dateRange[0]} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<MetaItem title="Category" content={categories[0]} />
|
||||
<MetaItem title="Data Created" content={<Time date={dateCreated} />} />
|
||||
|
||||
<MetaItem
|
||||
title="Data Published"
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import React, { ReactElement, ReactNode } from 'react'
|
||||
import styles from './MetaItem.module.css'
|
||||
|
||||
export default function MetaItem({
|
||||
@ -6,7 +6,7 @@ export default function MetaItem({
|
||||
content
|
||||
}: {
|
||||
title: string
|
||||
content: any
|
||||
content: ReactNode
|
||||
}): ReactElement {
|
||||
return (
|
||||
<div className={styles.metaItem}>
|
||||
|
@ -12,6 +12,5 @@
|
||||
}
|
||||
|
||||
.samples {
|
||||
composes: box from '../../atoms/Box.module.css';
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
}
|
||||
|
@ -4,16 +4,14 @@ import { ListItem } from '../../atoms/Lists'
|
||||
import MetaItem from './MetaItem'
|
||||
import styles from './MetaSecondary.module.css'
|
||||
import { MetaDataMarket } from '../../../@types/MetaData'
|
||||
import Tags from '../../atoms/Tags'
|
||||
|
||||
export default function MetaSecondary({
|
||||
metadata
|
||||
}: {
|
||||
metadata: MetaDataMarket
|
||||
}): ReactElement {
|
||||
let links
|
||||
if (metadata && metadata.additionalInformation) {
|
||||
;({ links } = metadata.additionalInformation)
|
||||
}
|
||||
const { links, tags } = metadata.additionalInformation
|
||||
|
||||
return (
|
||||
<aside className={styles.metaSecondary}>
|
||||
@ -33,6 +31,8 @@ export default function MetaSecondary({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tags && tags.length > 0 && <Tags items={tags} />}
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
}
|
@ -1,13 +1,17 @@
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: calc(var(--spacer) * 1.5);
|
||||
gap: calc(var(--spacer) * 2);
|
||||
position: relative;
|
||||
margin-top: -1.5rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
composes: box from '../../atoms/Box.module.css';
|
||||
margin-top: var(--spacer);
|
||||
}
|
||||
|
||||
@media (min-width: 60rem) {
|
||||
.grid {
|
||||
gap: calc(var(--spacer) * 3);
|
||||
/* lazy golden ratio */
|
||||
grid-template-columns: 1.618fr minmax(0, 1fr);
|
||||
}
|
||||
@ -15,7 +19,7 @@
|
||||
.sticky {
|
||||
position: sticky;
|
||||
top: calc(var(--spacer) / 2);
|
||||
margin-top: calc(var(--spacer) * 1.5);
|
||||
margin-top: var(--spacer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { MetaDataMarket } from '../../../@types/MetaData'
|
||||
import React, { ReactElement } from 'react'
|
||||
import { useOcean } from '@oceanprotocol/react'
|
||||
import Time from '../../atoms/Time'
|
||||
import { Link } from 'gatsby'
|
||||
import Markdown from '../../atoms/Markdown'
|
||||
import Tags from '../../atoms/Tags'
|
||||
import MetaFull from './MetaFull'
|
||||
import MetaSecondary from './MetaSecondary'
|
||||
import styles from './index.module.css'
|
||||
@ -21,42 +19,24 @@ export default function AssetContent({
|
||||
did
|
||||
}: AssetContentProps): ReactElement {
|
||||
const { datePublished } = metadata.main
|
||||
const { description, categories, tags } = 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)
|
||||
// }
|
||||
const { description, categories } = metadata.additionalInformation
|
||||
|
||||
return (
|
||||
<article className={styles.grid}>
|
||||
<div>
|
||||
<div className={styles.content}>
|
||||
<aside className={styles.meta}>
|
||||
<p>{datePublished && <Time date={datePublished} />}</p>
|
||||
{categories && (
|
||||
<p>
|
||||
<Link to={`/search?categories=["${categories[0]}"]`}>
|
||||
<a>{categories[0]}</a>
|
||||
{categories[0]}
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
{/* <Rating curation={{ rating, numVotes }} readonly /> */}
|
||||
</aside>
|
||||
|
||||
<Markdown text={description || ''} />
|
||||
|
||||
{tags && tags.length > 0 && <Tags items={tags} />}
|
||||
|
||||
<MetaSecondary metadata={metadata} />
|
||||
|
||||
<MetaFull did={did} metadata={metadata} />
|
||||
@ -74,8 +54,6 @@ export default function AssetContent({
|
||||
<div>
|
||||
<div className={styles.sticky}>
|
||||
<AssetActions metadata={metadata} did={did} />
|
||||
|
||||
{/* <RatingAction did={did} onVote={onVoteUpdate} /> */}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
@ -10,3 +10,9 @@
|
||||
gap: var(--spacer);
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: var(--color-secondary);
|
||||
font-size: var(--font-size-small);
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { DDO } from '@oceanprotocol/squid'
|
||||
import { DDO } from '@oceanprotocol/lib'
|
||||
import AssetList from './AssetList'
|
||||
import asset from '../../../tests/unit/__fixtures__/ddo'
|
||||
|
||||
|
@ -1,64 +1,77 @@
|
||||
import AssetTeaser from '../molecules/AssetTeaser'
|
||||
import React from 'react'
|
||||
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
|
||||
import shortid from 'shortid'
|
||||
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatastore/MetadataStore'
|
||||
import { useLocation, useNavigate } from '@reach/router'
|
||||
import Pagination from '../molecules/Pagination'
|
||||
import { updateQueryStringParameter } from '../../utils'
|
||||
import styles from './AssetList.module.css'
|
||||
import { MetaDataMarket } from '../../@types/MetaData'
|
||||
import { DDO } from '@oceanprotocol/squid'
|
||||
import { DDO } from '@oceanprotocol/lib'
|
||||
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||
|
||||
declare type AssetListProps = {
|
||||
queryResult: 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,
|
||||
// since the links are no <Link> they will not work by itself.
|
||||
// function hrefBuilder(pageIndex: number) {
|
||||
// const newUrl = updateQueryStringParameter(
|
||||
// router.asPath,
|
||||
// 'page',
|
||||
// `${pageIndex}`
|
||||
// )
|
||||
// return newUrl
|
||||
// }
|
||||
function hrefBuilder(pageIndex: number) {
|
||||
const newUrl = updateQueryStringParameter(
|
||||
location.pathname + location.search,
|
||||
'page',
|
||||
`${pageIndex}`
|
||||
)
|
||||
return newUrl
|
||||
}
|
||||
|
||||
// // This is what iniitates a new search with new `page`
|
||||
// // url parameter
|
||||
// function onPageChange(selected: number) {
|
||||
// const newUrl = updateQueryStringParameter(
|
||||
// router.asPath,
|
||||
// 'page',
|
||||
// `${selected + 1}`
|
||||
// )
|
||||
// return router.push(newUrl)
|
||||
// }
|
||||
function onPageChange(selected: number) {
|
||||
const newUrl = updateQueryStringParameter(
|
||||
location.pathname + location.search,
|
||||
'page',
|
||||
`${selected + 1}`
|
||||
)
|
||||
return navigate(newUrl)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.assetList}>
|
||||
{queryResult &&
|
||||
{queryResult && queryResult.totalResults > 0 ? (
|
||||
queryResult.results.map((ddo: DDO) => {
|
||||
const { attributes }: MetaDataMarket = new DDO(
|
||||
ddo
|
||||
).findServiceByType('metadata')
|
||||
const { attributes }: MetaDataMarket = ddo.findServiceByType(
|
||||
'metadata'
|
||||
)
|
||||
|
||||
return (
|
||||
<AssetTeaser
|
||||
did={ddo.id}
|
||||
metadata={attributes}
|
||||
key={shortid.generate()}
|
||||
/>
|
||||
<AssetTeaser did={ddo.id} metadata={attributes} key={ddo.id} />
|
||||
)
|
||||
})}
|
||||
})
|
||||
) : (
|
||||
<div className={styles.empty}>
|
||||
No results found in {appConfig.oceanConfig.metadataStoreUri}
|
||||
</div>
|
||||
{/* <Pagination
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/*
|
||||
Little hack cause the pagination navigation only works
|
||||
on the search page right now.
|
||||
*/}
|
||||
{location.pathname === '/search' && queryResult && (
|
||||
<Pagination
|
||||
totalPages={queryResult.totalPages}
|
||||
currentPage={queryResult.page}
|
||||
hrefBuilder={hrefBuilder}
|
||||
onPageChange={onPageChange}
|
||||
/> */}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import Price from '../atoms/Price'
|
||||
import { fromWei } from 'web3-utils'
|
||||
import DateCell from '../atoms/Table/DateCell'
|
||||
import DdoLinkCell from '../atoms/Table/DdoLinkCell'
|
||||
import { MetaDataMain } from '@oceanprotocol/squid'
|
||||
import { MetaDataMain } from '@oceanprotocol/lib'
|
||||
|
||||
const consumedColumns = [
|
||||
{
|
||||
|
@ -1,3 +1,2 @@
|
||||
.header {
|
||||
background-color: var(--brand-white);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import Price from '../atoms/Price'
|
||||
import { fromWei } from 'web3-utils'
|
||||
import Table from '../atoms/Table'
|
||||
import Button from '../atoms/Button'
|
||||
import { MetaDataMain, Logger } from '@oceanprotocol/squid'
|
||||
import { MetaDataMain, Logger } from '@oceanprotocol/lib'
|
||||
import DateCell from '../atoms/Table/DateCell'
|
||||
import DdoLinkCell from '../atoms/Table/DdoLinkCell'
|
||||
import shortid from 'shortid'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState, ReactElement } from 'react'
|
||||
import Loader from '../atoms/Loader'
|
||||
import { MetaDataMain } from '@oceanprotocol/squid'
|
||||
import { MetaDataMain } from '@oceanprotocol/lib'
|
||||
import {
|
||||
useOcean,
|
||||
OceanConnectionStatus,
|
||||
|
@ -15,3 +15,8 @@
|
||||
top: var(--spacer);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
composes: box from '../atoms/Box.module.css';
|
||||
margin-top: var(--spacer);
|
||||
}
|
||||
|
@ -8,15 +8,15 @@ import JobsList from '../organisms/JobsList'
|
||||
const sections = [
|
||||
{
|
||||
title: 'Published',
|
||||
component: <PublishedList />
|
||||
component: 'Coming Soon...'
|
||||
},
|
||||
{
|
||||
title: 'Downloaded',
|
||||
component: <ConsumedList />
|
||||
component: 'Coming Soon...'
|
||||
},
|
||||
{
|
||||
title: 'Compute Jobs',
|
||||
component: <JobsList />
|
||||
component: 'Coming Soon...'
|
||||
}
|
||||
]
|
||||
|
||||
@ -32,7 +32,7 @@ const Section = ({ title, component }: { title: string; component: any }) => {
|
||||
const HistoryPage: React.FC = () => {
|
||||
return (
|
||||
<article className={styles.grid}>
|
||||
<div>
|
||||
<div className={styles.content}>
|
||||
{sections.map((section) => {
|
||||
const { title, component } = section
|
||||
return <Section key={title} title={title} component={component} />
|
||||
|
@ -1,3 +1,14 @@
|
||||
.grid {
|
||||
composes: assetList from '../organisms/AssetList.module.css';
|
||||
.searchWrap {
|
||||
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);
|
||||
}
|
||||
|
@ -1,31 +1,60 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
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 { 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 (
|
||||
<>
|
||||
<Container narrow className={styles.searchWrap}>
|
||||
<SearchBar large />
|
||||
{assets && (
|
||||
<div className={styles.grid}>
|
||||
{assets.map(({ node }: { node: OceanAsset }) => (
|
||||
<AssetTeaser
|
||||
key={shortid.generate()}
|
||||
did={node.did}
|
||||
metadata={node}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<section className={styles.latest}>
|
||||
<h3>Latest Data Sets</h3>
|
||||
{loading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
queryResult && <AssetList queryResult={queryResult} />
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { useOcean } from '@oceanprotocol/react'
|
||||
import {
|
||||
Service,
|
||||
ServiceCompute
|
||||
} from '@oceanprotocol/squid/dist/node/ddo/Service'
|
||||
} from '@oceanprotocol/lib/dist/node/ddo/Service'
|
||||
import { Formik, Form as FormFormik, Field } from 'formik'
|
||||
import Input from '../../atoms/Input'
|
||||
import Button from '../../atoms/Button'
|
||||
@ -15,7 +15,7 @@ import { transformPublishFormToMetadata } from './utils'
|
||||
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
||||
import { MetaDataPublishForm } from '../../../@types/MetaData'
|
||||
import AssetModel from '../../../models/Asset'
|
||||
import { File } from '@oceanprotocol/squid'
|
||||
import { File } from '@oceanprotocol/lib'
|
||||
|
||||
const validationSchema = Yup.object().shape<MetaDataPublishForm>({
|
||||
// ---- required fields ----
|
||||
@ -81,7 +81,7 @@ export default function PublishForm({
|
||||
// account,
|
||||
// metadata.main.price,
|
||||
// // 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.
|
||||
// toStringNoMS(new Date(Date.now()))
|
||||
// )
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
@ -1,8 +1,3 @@
|
||||
.empty {
|
||||
color: var(--color-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
@ -10,17 +5,21 @@
|
||||
@media (min-width: 55rem) {
|
||||
.grid {
|
||||
grid-column-gap: calc(var(--spacer) * 3);
|
||||
grid-template-columns: minmax(0, 1fr) 3fr;
|
||||
grid-template-columns: minmax(0, 3fr) 1fr;
|
||||
grid-template-areas:
|
||||
'side search'
|
||||
'side results';
|
||||
'search side'
|
||||
'results side';
|
||||
}
|
||||
|
||||
.search {
|
||||
grid-area: search;
|
||||
}
|
||||
|
||||
.side {
|
||||
grid-area: side;
|
||||
margin-top: calc(var(--spacer) * 2.5);
|
||||
}
|
||||
|
||||
.results {
|
||||
grid-area: results;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
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 AssetList from '../../organisms/AssetList'
|
||||
import { SearchPriceFilter } from '../../molecules/SearchPriceFilter'
|
||||
|
||||
import styles from './index.module.css'
|
||||
import queryString from 'query-string'
|
||||
import { getResults } from './utils'
|
||||
import Loader from '../../atoms/Loader'
|
||||
|
||||
export declare type SearchPageProps = {
|
||||
text: string | string[]
|
||||
@ -20,16 +20,19 @@ export default function SearchPage({
|
||||
location: Location
|
||||
}): ReactElement {
|
||||
const parsed = queryString.parse(location.search)
|
||||
const { text, tag } = parsed
|
||||
const { text, tag, page } = parsed
|
||||
const [queryResult, setQueryResult] = useState<QueryResult>()
|
||||
const [loading, setLoading] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
async function initSearch() {
|
||||
setLoading(true)
|
||||
const queryResult = await getResults(parsed)
|
||||
setQueryResult(queryResult)
|
||||
setLoading(false)
|
||||
}
|
||||
initSearch()
|
||||
}, [parsed])
|
||||
}, [text, tag, page])
|
||||
|
||||
return (
|
||||
<section className={styles.grid}>
|
||||
@ -42,11 +45,7 @@ export default function SearchPage({
|
||||
</aside>
|
||||
|
||||
<div className={styles.results}>
|
||||
{queryResult && queryResult.results.length > 0 ? (
|
||||
<AssetList queryResult={queryResult} />
|
||||
) : (
|
||||
<div className={styles.empty}>No results found.</div>
|
||||
)}
|
||||
{loading ? <Loader /> : <AssetList queryResult={queryResult} />}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {
|
||||
SearchQuery,
|
||||
QueryResult
|
||||
} from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
|
||||
} from '@oceanprotocol/lib/dist/node/metadatastore/MetadataStore'
|
||||
import { priceQueryParamToWei } from '../../../utils'
|
||||
import { Aquarius, Logger } from '@oceanprotocol/squid'
|
||||
import { MetadataStore, Logger } from '@oceanprotocol/lib'
|
||||
import { oceanConfig } from '../../../../app.config'
|
||||
|
||||
export function getSearchQuery(
|
||||
@ -52,8 +52,8 @@ export async function getResults(params: any): Promise<QueryResult> {
|
||||
])
|
||||
: undefined
|
||||
|
||||
const aquarius = new Aquarius(oceanConfig.aquariusUri, Logger)
|
||||
const queryResult = await aquarius.queryMetadata(
|
||||
const metadataStore = new MetadataStore(oceanConfig.metadataStoreUri, Logger)
|
||||
const queryResult = await metadataStore.queryMetadata(
|
||||
getSearchQuery(page, offset, text, tag, priceQuery)
|
||||
)
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { ToastContainer } from 'react-toastify'
|
||||
|
||||
import '@oceanprotocol/typographies/css/ocean-typo.css'
|
||||
import '../global/styles.css'
|
||||
import 'react-toastify/dist/ReactToastify.css'
|
||||
|
||||
export default function Styles({
|
||||
children
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import '../../node_modules/react-toastify/dist/ReactToastify.css';
|
||||
|
||||
div.Toastify__toast {
|
||||
font-family: var(--font-family-base);
|
||||
font-size: var(--font-size-small);
|
||||
|
68
src/global/_web3modal.css
Normal file
68
src/global/_web3modal.css
Normal 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);
|
||||
}
|
||||
}
|
@ -134,3 +134,4 @@ fieldset {
|
||||
|
||||
@import '_code.css';
|
||||
@import '_toast.css';
|
||||
@import '_web3modal.css';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import { Web3Provider, OceanProvider, Config } from '@oceanprotocol/react'
|
||||
import { OceanProvider } from '@oceanprotocol/react'
|
||||
import { oceanConfig } from '../../app.config'
|
||||
|
||||
const wrapRootElement = ({
|
||||
@ -7,9 +7,7 @@ const wrapRootElement = ({
|
||||
}: {
|
||||
element: ReactElement
|
||||
}): ReactElement => (
|
||||
<Web3Provider>
|
||||
<OceanProvider config={oceanConfig as Config}>{element}</OceanProvider>
|
||||
</Web3Provider>
|
||||
<OceanProvider config={oceanConfig}>{element}</OceanProvider>
|
||||
)
|
||||
|
||||
export default wrapRootElement
|
||||
|
@ -2,15 +2,36 @@ import { useStaticQuery, graphql } from 'gatsby'
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
siteMetadata: allFile(filter: { relativePath: { eq: "site.json" } }) {
|
||||
edges {
|
||||
node {
|
||||
childContentJson {
|
||||
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 {
|
||||
node {
|
||||
childContentJson {
|
||||
site {
|
||||
siteImage {
|
||||
childImageSharp {
|
||||
original {
|
||||
@ -18,11 +39,6 @@ const query = graphql`
|
||||
}
|
||||
}
|
||||
}
|
||||
copyright
|
||||
menu {
|
||||
name
|
||||
link
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,5 +49,11 @@ const query = graphql`
|
||||
|
||||
export function useSiteMetadata() {
|
||||
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
|
||||
}
|
||||
|
@ -4,11 +4,13 @@ import AssetContent from '../../components/organisms/AssetContent'
|
||||
import Layout from '../../components/Layout'
|
||||
import { PageProps } from 'gatsby'
|
||||
import { MetaDataMarket, ServiceMetaDataMarket } from '../../@types/MetaData'
|
||||
import { Aquarius, Logger } from '@oceanprotocol/squid'
|
||||
import { oceanConfig } from '../../../app.config'
|
||||
import { MetadataStore, Logger } from '@oceanprotocol/lib'
|
||||
import Alert from '../../components/atoms/Alert'
|
||||
import Loader from '../../components/atoms/Loader'
|
||||
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||
|
||||
export default function AssetRoute(props: PageProps): ReactElement {
|
||||
const { appConfig } = useSiteMetadata()
|
||||
const [metadata, setMetadata] = useState<MetaDataMarket>()
|
||||
const [title, setTitle] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
@ -18,12 +20,15 @@ export default function AssetRoute(props: PageProps): ReactElement {
|
||||
useEffect(() => {
|
||||
async function init() {
|
||||
try {
|
||||
const aquarius = new Aquarius(oceanConfig.aquariusUri, Logger)
|
||||
const ddo = await aquarius.retrieveDDO(did)
|
||||
const metadataStore = new MetadataStore(
|
||||
appConfig.oceanConfig.metadataStoreUri,
|
||||
Logger
|
||||
)
|
||||
const ddo = await metadataStore.retrieveDDO(did)
|
||||
|
||||
if (!ddo) {
|
||||
setTitle('Could not retrieve asset')
|
||||
setError('The DDO was not found in Aquarius.')
|
||||
setError('The DDO was not found in MetadataStore.')
|
||||
return
|
||||
}
|
||||
|
||||
@ -39,21 +44,25 @@ export default function AssetRoute(props: PageProps): ReactElement {
|
||||
}
|
||||
}
|
||||
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}>
|
||||
<Alert title={title} text={error} state="error" />
|
||||
</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}>
|
||||
Loading...
|
||||
<Loader />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
@ -1,40 +1,20 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import { PageProps, graphql } from 'gatsby'
|
||||
import { PageProps } from 'gatsby'
|
||||
import PageHome from '../components/pages/Home'
|
||||
import { useSiteMetadata } from '../hooks/useSiteMetadata'
|
||||
import Layout from '../components/Layout'
|
||||
|
||||
export default function PageGatsbyHome(props: PageProps): ReactElement {
|
||||
const { siteTitle, siteTagline } = useSiteMetadata()
|
||||
const assets = (props.data as any).allOceanAsset.edges
|
||||
|
||||
return (
|
||||
<Layout title={siteTitle} description={siteTagline} uri={props.uri}>
|
||||
<PageHome assets={assets} />
|
||||
<Layout
|
||||
title={siteTitle}
|
||||
description={siteTagline}
|
||||
uri={props.uri}
|
||||
headerCenter
|
||||
>
|
||||
<PageHome />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export const pageQuery = graphql`
|
||||
query PageHomeQuery {
|
||||
allOceanAsset {
|
||||
edges {
|
||||
node {
|
||||
did
|
||||
main {
|
||||
type
|
||||
name
|
||||
dateCreated
|
||||
author
|
||||
price
|
||||
datePublished
|
||||
}
|
||||
additionalInformation {
|
||||
description
|
||||
access
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import axios, { AxiosResponse } from 'axios'
|
||||
import { toast } from 'react-toastify'
|
||||
import { File } from '@oceanprotocol/squid'
|
||||
import { File } from '@oceanprotocol/lib'
|
||||
import numeral from 'numeral'
|
||||
import web3Utils from 'web3-utils'
|
||||
|
||||
|
@ -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
55
src/utils/wallet.ts
Normal 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'
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { DDO } from '@oceanprotocol/squid'
|
||||
import { DDO } from '@oceanprotocol/lib'
|
||||
import { MetaDataMarket } from '../../../src/@types/MetaData'
|
||||
|
||||
const ddo: Partial<DDO> = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ComputeJob } from '@oceanprotocol/squid'
|
||||
import { ComputeJob } from '@oceanprotocol/lib'
|
||||
|
||||
// ComputeJob need to be updated in squid
|
||||
const job: Partial<ComputeJob> = {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import ddo from '../../__fixtures__/ddo'
|
||||
import job from '../../__fixtures__/job'
|
||||
|
||||
const aquarius = {
|
||||
const metadataStore = {
|
||||
queryMetadata: () => {
|
||||
return {
|
||||
results: [] as any[],
|
||||
@ -12,13 +12,13 @@ const aquarius = {
|
||||
}
|
||||
|
||||
const squidMock = {
|
||||
Aquarius: () => aquarius,
|
||||
MetadataStore: () => metadataStore,
|
||||
DDO: () => ddo,
|
||||
ocean: {
|
||||
accounts: {
|
||||
list: () => ['xxx', 'xxx']
|
||||
},
|
||||
aquarius,
|
||||
metadataStore,
|
||||
compute: {
|
||||
status: (account: string) => {
|
||||
return [job]
|
||||
@ -69,13 +69,13 @@ const squidMock = {
|
||||
name: 'Squid-js',
|
||||
status: 'Working'
|
||||
},
|
||||
aquarius: {
|
||||
name: 'Aquarius',
|
||||
metadataStore: {
|
||||
name: 'MetadataStore',
|
||||
status: 'Working'
|
||||
},
|
||||
brizo: {
|
||||
name: 'Brizo',
|
||||
network: 'Nile',
|
||||
provider: {
|
||||
name: 'Provider',
|
||||
network: 'Rinkeby',
|
||||
status: 'Working',
|
||||
contracts: {
|
||||
hello: 'hello',
|
@ -1,5 +1,5 @@
|
||||
import React, { ReactElement } from 'react'
|
||||
import squidMock from './squid'
|
||||
import libMock from './lib'
|
||||
import web3ProviderMock from '../web3provider'
|
||||
|
||||
const reactMock = {
|
||||
@ -19,7 +19,7 @@ const reactMock = {
|
||||
},
|
||||
useOcean: () => {
|
||||
return {
|
||||
ocean: squidMock.ocean
|
||||
ocean: libMock.ocean
|
||||
}
|
||||
},
|
||||
useWeb3: () => {
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
@ -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)
|
||||
})
|
||||
})
|
@ -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: ')
|
||||
})
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user