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

refactor Next.js pages into Gatsby pages

This commit is contained in:
Matthias Kretschmann 2020-06-30 12:56:50 +02:00
parent 9cc38304c8
commit 6f9bf0176f
Signed by: m
GPG Key ID: 606EEEF3C479A91F
55 changed files with 480 additions and 989 deletions

View File

@ -27,6 +27,7 @@
"plugins": ["@typescript-eslint", "prettier"], "plugins": ["@typescript-eslint", "prettier"],
"rules": { "rules": {
"react/prop-types": "off", "react/prop-types": "off",
"react/no-unused-prop-types": "off",
"@typescript-eslint/explicit-function-return-type": "off" "@typescript-eslint/explicit-function-return-type": "off"
} }
} }

1
.gitignore vendored
View File

@ -1,7 +1,6 @@
node_modules node_modules
out out
.DS_Store .DS_Store
.next
.idea .idea
.env .env
coverage coverage

View File

@ -0,0 +1,4 @@
{
"title": "History",
"description": "Find the data sets and jobs that you previously accessed."
}

View File

@ -0,0 +1,4 @@
{
"title": "Publish Data",
"description": "Highlight the important features of your data set to make it more discoverable and catch the interest of data consumers."
}

View File

@ -1,10 +1,10 @@
require('dotenv').config() require('dotenv').config()
const siteConfig = require('./site.config.js') const siteConfig = require('./content/site.json')
module.exports = { module.exports = {
siteMetadata: { siteMetadata: {
...siteConfig ...siteConfig.site
}, },
plugins: [ plugins: [
{ {

54
package-lock.json generated
View File

@ -6009,6 +6009,36 @@
"@types/yargs": "^13.0.0" "@types/yargs": "^13.0.0"
} }
}, },
"@loadable/component": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.13.0.tgz",
"integrity": "sha512-+qS6xoA4DN5Pjm5sR6vWuuYEhMWZ9Y7ALgI8JuDOMCiqOG+PTau0phOyqFgrFqDXijVpeInoIoZQdW1FKjGBYA==",
"requires": {
"@babel/runtime": "^7.7.7",
"hoist-non-react-statics": "^3.3.1",
"react-is": "^16.12.0"
},
"dependencies": {
"@babel/runtime": {
"version": "7.10.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.3.tgz",
"integrity": "sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"regenerator-runtime": {
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
}
}
},
"@mdx-js/mdx": { "@mdx-js/mdx": {
"version": "1.6.6", "version": "1.6.6",
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.6.tgz", "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.6.tgz",
@ -32909,8 +32939,7 @@
"react-fast-compare": { "react-fast-compare": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
"dev": true
}, },
"react-focus-lock": { "react-focus-lock": {
"version": "2.4.0", "version": "2.4.0",
@ -32926,6 +32955,17 @@
"use-sidecar": "^1.0.1" "use-sidecar": "^1.0.1"
} }
}, },
"react-helmet": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
"integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
"requires": {
"object-assign": "^4.1.1",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.1.1",
"react-side-effect": "^2.1.0"
}
},
"react-helmet-async": { "react-helmet-async": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.0.6.tgz", "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.0.6.tgz",
@ -33126,6 +33166,11 @@
"no-scroll": "^2.1.1" "no-scroll": "^2.1.1"
} }
}, },
"react-side-effect": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.0.tgz",
"integrity": "sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg=="
},
"react-sizeme": { "react-sizeme": {
"version": "2.6.12", "version": "2.6.12",
"resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-2.6.12.tgz", "resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-2.6.12.tgz",
@ -37855,11 +37900,6 @@
"integrity": "sha512-rXpsyvOnqdScyied4Uglsp14qzag1JIemLeTWGKbwpotWht57hbP78aNT+Q4wdFKQfQibbUX4fb6Qb4y11aVOQ==", "integrity": "sha512-rXpsyvOnqdScyied4Uglsp14qzag1JIemLeTWGKbwpotWht57hbP78aNT+Q4wdFKQfQibbUX4fb6Qb4y11aVOQ==",
"dev": true "dev": true
}, },
"use-debounce": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-3.4.2.tgz",
"integrity": "sha512-rW44wZaFPh3XiwUzGBA0JRuklpbfKO/szU/5CYD2Q/erLmCem63lJ650p3a+NJE6S+g0rulKtBOfa/3rw/GN+Q=="
},
"use-sidecar": { "use-sidecar": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.2.tgz", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.2.tgz",

View File

@ -19,6 +19,7 @@
"storybook:build": "build-storybook -c .storybook -o public/storybook" "storybook:build": "build-storybook -c .storybook -o public/storybook"
}, },
"dependencies": { "dependencies": {
"@loadable/component": "^5.13.0",
"@oceanprotocol/art": "^2.2.0", "@oceanprotocol/art": "^2.2.0",
"@oceanprotocol/react": "0.0.11", "@oceanprotocol/react": "0.0.11",
"@oceanprotocol/squid": "^2.2.0", "@oceanprotocol/squid": "^2.2.0",
@ -48,6 +49,7 @@
"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.1",
"react-helmet": "^6.1.0",
"react-jsonschema-form": "^1.8.1", "react-jsonschema-form": "^1.8.1",
"react-markdown": "^4.3.1", "react-markdown": "^4.3.1",
"react-paginate": "^6.3.2", "react-paginate": "^6.3.2",
@ -56,7 +58,6 @@
"react-toastify": "^6.0.6", "react-toastify": "^6.0.6",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"slugify": "^1.4.4", "slugify": "^1.4.4",
"use-debounce": "^3.4.2",
"web3connect": "^1.0.0-beta.33" "web3connect": "^1.0.0-beta.33"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,11 +1,11 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { WidgetProps } from 'react-jsonschema-form' import { WidgetProps } from 'react-jsonschema-form'
import dynamic from 'next/dynamic' import loadable from '@loadable/component'
import styles from './DateRangeWidget.module.css' import styles from './DateRangeWidget.module.css'
import { toStringNoMS } from '../../../utils' import { toStringNoMS } from '../../../utils'
// lazy load this module, it's huge // lazy load this module, it's huge
const LazyDatePicker = dynamic(() => import('react-datepicker')) const LazyDatePicker = loadable(() => import('react-datepicker'))
export function getWidgetValue( export function getWidgetValue(
date1: Date, date1: Date,

View File

@ -1,8 +1,6 @@
import React from 'react' import React from 'react'
import { WidgetProps } from 'react-jsonschema-form' import { WidgetProps } from 'react-jsonschema-form'
import styles from './TermsWidget.module.css' import styles from './TermsWidget.module.css'
import Markdown from '../Markdown'
import terms from '../../../../content/terms.md'
export default function TermsWidget(props: WidgetProps) { export default function TermsWidget(props: WidgetProps) {
const { const {
@ -21,7 +19,7 @@ export default function TermsWidget(props: WidgetProps) {
return ( return (
<> <>
<Markdown text={terms} className={styles.terms} /> {/* <Markdown text={terms} className={styles.terms} /> */}
<label <label
htmlFor={id} htmlFor={id}
className={required ? `${styles.label} ${styles.req}` : styles.label} className={required ? `${styles.label} ${styles.req}` : styles.label}

View File

@ -1,5 +1,5 @@
.help { .help {
font-size: var(--font-size-small); font-size: var(--font-size-small);
color: var(--brand-grey-light); color: var(--brand-grey-light);
margin-top: var(--spacer) / 4; margin-top: calc(var(--spacer) / 6);
} }

View File

@ -1,213 +0,0 @@
import cx from 'classnames'
import React, { PureComponent, FormEvent, ChangeEvent } from 'react'
import slugify from '@sindresorhus/slugify'
import DatePicker from 'react-datepicker'
import Help from './Help'
import Label from './Label'
import Row from './Row'
import InputGroup from './InputGroup'
import styles from './Input.module.css'
interface InputProps {
name: string
label: string
placeholder?: string
required?: boolean
help?: string
tag?: string
type?: string
options?: string[]
additionalComponent?: any
value?: string
onChange?(
event:
| FormEvent<HTMLInputElement>
| ChangeEvent<HTMLInputElement>
| ChangeEvent<HTMLSelectElement>
| ChangeEvent<HTMLTextAreaElement>
): void
rows?: number
group?: any
multiple?: boolean
pattern?: string
}
interface InputState {
isFocused: boolean
dateCreated?: Date
}
export default class Input extends PureComponent<InputProps, InputState> {
public state: InputState = {
isFocused: false,
dateCreated: new Date()
}
public inputWrapClasses() {
if (this.props.type === 'search') {
return styles.inputWrapSearch
} else if (this.props.type === 'search' && this.state.isFocused) {
return cx(styles.inputWrapSearch, styles.isFocused)
} else if (this.state.isFocused && this.props.type !== 'search') {
return cx(styles.inputWrap, styles.isFocused)
} else {
return styles.inputWrap
}
}
public handleFocus = () => {
this.setState({ isFocused: !this.state.isFocused })
}
private handleDateChange = (date: Date) => {
this.setState({ dateCreated: date })
const event = {
currentTarget: {
name: 'dateCreated',
value: date
}
}
this.props.onChange!(event as any) // eslint-disable-line @typescript-eslint/no-non-null-assertion
}
public InputComponent = () => {
const { type, options, group, name, required, onChange, value } = this.props
const wrapClass = this.inputWrapClasses()
switch (type) {
case 'select':
return (
<div className={wrapClass}>
<select
id={name}
className={styles.select}
name={name}
required={required}
onFocus={this.handleFocus}
onBlur={this.handleFocus}
onChange={onChange}
value={value}
>
<option value="">---</option>
{options &&
options
.sort((a, b) => a.localeCompare(b))
.map((option: string, index: number) => (
<option key={index} value={option}>
{option}
</option>
))}
</select>
</div>
)
case 'textarea':
return (
<div className={wrapClass}>
<textarea
id={name}
className={styles.input}
onFocus={this.handleFocus}
onBlur={this.handleFocus}
{...this.props}
/>
</div>
)
case 'radio':
case 'checkbox':
return (
<div className={styles.radioGroup}>
{options &&
options.map((option: string, index: number) => (
<div className={styles.radioWrap} key={index}>
<input
className={styles.radio}
id={slugify(option)}
type={type}
name={name}
value={slugify(option)}
/>
<label
className={styles.radioLabel}
htmlFor={slugify(option)}
>
{option}
</label>
</div>
))}
</div>
)
case 'date':
return (
<div className={wrapClass}>
<DatePicker
selected={this.state.dateCreated}
onChange={this.handleDateChange}
className={styles.input}
onFocus={this.handleFocus}
onBlur={this.handleFocus}
id={name}
name={name}
/>
</div>
)
default:
return (
<div className={wrapClass}>
{group ? (
<InputGroup>
<input
id={name}
type={type || 'text'}
className={styles.input}
onFocus={this.handleFocus}
onBlur={this.handleFocus}
{...this.props}
/>
{group}
</InputGroup>
) : (
<input
id={name}
type={type || 'text'}
className={styles.input}
onFocus={this.handleFocus}
onBlur={this.handleFocus}
{...this.props}
/>
)}
{/* {type === 'search' && <SearchIcon />} */}
</div>
)
}
}
public render() {
const {
name,
label,
required,
help,
additionalComponent,
multiple
} = this.props
return (
<Row>
<Label htmlFor={name} required={required}>
{label}
</Label>
<this.InputComponent />
{help && <Help>{help}</Help>}
{multiple && 'hello'}
{additionalComponent && additionalComponent}
</Row>
)
}
}

View File

@ -1,63 +1,27 @@
.inputWrap,
.inputWrapSearch {
background: var(--brand-gradient);
border-radius: var(--border-radius);
padding: 2px;
display: flex;
position: relative;
}
.inputWrap .isFocused,
.inputWrapSearch .isFocused {
background: var(--brand-black);
}
.inputWrap > div,
.inputWrap > div > div,
.inputWrapSearch > div,
.inputWrapSearch > div > div {
width: 100%;
}
.inputWrapSearch,
.input { .input {
padding-left: var(--spacer) * 1.5;
}
.inputWrapSearch svg {
position: absolute;
left: var(--spacer) / 2;
width: 1.25rem;
height: 1.25rem;
top: 50%;
margin-top: -0.6rem;
fill: rgba(var(--brand-grey-light), 0.7);
}
.input,
.select {
font-size: var(--font-size-base); font-size: var(--font-size-base);
font-family: var(--font-family-base); font-family: var(--font-family-base);
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
color: var(--brand-black); color: var(--brand-black);
border: none; border: 1px solid var(--brand-grey-lighter);
box-shadow: none; box-shadow: none;
width: 100%; width: 100%;
background: var(--brand-white); background: var(--brand-white);
padding: var(--spacer) / 3; padding: calc(var(--spacer) / 3);
margin: 0; margin: 0;
border-radius: var(--border-radius); border-radius: var(--border-radius);
transition: 0.2s ease-out; transition: 0.2s ease-out;
min-height: 43px; min-height: 43px;
appearance: none; appearance: none;
display: block;
} }
.input:focus,
.select:focus { .input:focus {
border: none; border-color: var(--brand-grey);
box-shadow: none; box-shadow: none;
outline: 0; outline: 0;
} }
.select::placeholder,
.input::placeholder { .input::placeholder {
font-family: var(--font-family-base); font-family: var(--font-family-base);
font-size: var(--font-size-base); font-size: var(--font-size-base);
@ -67,9 +31,7 @@
opacity: 0.7; opacity: 0.7;
} }
.select[readonly],
.input[readonly], .input[readonly],
.select[disabled],
.input[disabled] { .input[disabled] {
background-color: var(--brand-grey-lighter); background-color: var(--brand-grey-lighter);
cursor: not-allowed; cursor: not-allowed;
@ -80,35 +42,38 @@
composes: input; composes: input;
height: 43px; height: 43px;
padding-right: 3rem; padding-right: 3rem;
border: 0;
/* custom arrow */
background-image: linear-gradient( background-image: linear-gradient(
45deg, 45deg,
transparent 50%, transparent 50%,
var(--brand-purple 50%) var(--brand-purple) 50%
), ),
linear-gradient(135deg, var(--brand-purple) 50%, transparent 50%), linear-gradient(135deg, var(--brand-grey) 50%, transparent 50%),
linear-gradient( linear-gradient(
to right, to right,
var(--brand-pink) 1px, var(--brand-grey-lighter) 1px,
lighten(var(--brand-grey-lighter), 5%) 2px, transparent 1px,
lighten(var(--brand-grey-lighter), 5%) transparent
); );
background-position: calc(100% - 18px) calc(1rem + 5px), background-position: calc(100% - 18px) calc(1rem + 5px),
calc(100% - 13px) calc(1rem + 5px), 100% 0; calc(100% - 13px) calc(1rem + 5px), 100% 0;
background-size: 5px 5px, 5px 5px, 2.5rem 3rem; background-size: 5px 5px, 5px 5px, 2.5rem 3rem;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
.select:focus { .select:focus {
outline: 0; outline: 0;
font-family: var(--font-family-base); font-family: var(--font-family-base);
} }
.radioGroup { .radioGroup {
margin-top: var(--spacer) / 2; margin-top: calc(var(--spacer) / 2);
margin-bottom: -2%; margin-bottom: -2%;
}
@media screen and (min-width: var(--break-point--small)) { @media screen and (min-width: 40rem) {
.radioGroup {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
@ -117,13 +82,15 @@
.radioWrap { .radioWrap {
position: relative; position: relative;
padding: var(--spacer) / 2; padding: calc(var(--spacer) / 2);
text-align: center; text-align: center;
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 2%; margin-bottom: 2%;
}
@media screen and (min-width: var(--break-point--small)) { @media screen and (min-width: 40rem) {
.radioWrap {
flex: 0 0 49%; flex: 0 0 49%;
} }
} }
@ -138,7 +105,7 @@
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
font-size: var(--font-size-small); font-size: var(--font-size-small);
line-height: 1.2; line-height: 1.2;
border: 2px solid var(--brand-grey-lighter); border: 1px solid var(--brand-grey-lighter);
border-radius: 0.2rem; border-radius: 0.2rem;
position: absolute; position: absolute;
left: 0; left: 0;
@ -152,13 +119,16 @@
align-items: center; align-items: center;
} }
.inputSmall { /* Size modifiers */
.small {
composes: input; composes: input;
font-size: var(--font-size-small); font-size: var(--font-size-small);
min-height: 32px; min-height: 32px;
padding: var(--spacer) / 4; padding: calc(var(--spacer) / 4);
} }
.inputSmall::placeholder {
.small::placeholder {
font-size: var(--font-size-small); font-size: var(--font-size-small);
} }
@ -166,6 +136,8 @@
composes: select; composes: select;
height: 32px; height: 32px;
padding-right: 2rem; padding-right: 2rem;
/* custom arrow */
background-position: calc(100% - 14px) 1rem, calc(100% - 9px) 1rem, 100% 0; background-position: calc(100% - 14px) 1rem, calc(100% - 9px) 1rem, 100% 0;
background-size: 5px 5px, 5px 5px, 2rem 3rem; background-size: 5px 5px, 5px 5px, 2rem 3rem;
} }

View File

@ -0,0 +1,66 @@
import React from 'react'
import slugify from '@sindresorhus/slugify'
import styles from './InputElement.module.css'
import { InputProps } from '.'
export default function InputElement(props: InputProps) {
const { type, options, rows, name } = props
switch (type) {
case 'select':
return (
<select id={name} className={styles.select} name={name} {...props}>
<option value="">---</option>
{options &&
options
.sort((a: string, b: string) => a.localeCompare(b))
.map((option: string, index: number) => (
<option key={index} value={option}>
{option}
</option>
))}
</select>
)
case 'textarea':
return (
<textarea
id={name}
className={styles.input}
rows={rows}
name={name}
{...props}
/>
)
case 'radio':
case 'checkbox':
return (
<div className={styles.radioGroup}>
{options &&
options.map((option: string, index: number) => (
<div className={styles.radioWrap} key={index}>
<input
className={styles.radio}
id={slugify(option)}
type={type}
name={name}
{...props}
/>
<label className={styles.radioLabel} htmlFor={slugify(option)}>
{option}
</label>
</div>
))}
</div>
)
default:
return (
<input
id={name}
type={type || 'text'}
className={styles.input}
name={name}
{...props}
/>
)
}
}

View File

@ -1,10 +1,10 @@
.label .required { .label {
color: var(--brand-grey); color: var(--brand-grey);
font-size: var(--font-size-base); font-size: var(--font-size-base);
font-family: var(--font-family-title); font-family: var(--font-family-title);
line-height: 1.2; line-height: 1.2;
display: block; display: block;
margin-bottom: var(--spacer) / 6; margin-bottom: calc(var(--spacer) / 4);
} }
.required:after { .required:after {

View File

@ -11,7 +11,7 @@ const Label = ({
htmlFor: string htmlFor: string
}) => ( }) => (
<label <label
className={required ? styles.required : styles.label} className={`${styles.label} ${required && styles.required}`}
title={required ? 'Required' : ''} title={required ? 'Required' : ''}
{...props} {...props}
> >

View File

@ -0,0 +1,3 @@
.field {
margin-bottom: var(--spacer);
}

View File

@ -0,0 +1,59 @@
import React, { FormEvent, ChangeEvent, ReactElement } from 'react'
import InputElement from './InputElement'
import Help from './Help'
import Label from './Label'
import styles from './index.module.css'
export interface InputProps {
name: string
label?: string
placeholder?: string
required?: boolean
help?: string
tag?: string
type?: string
options?: string[]
additionalComponent?: ReactElement
value?: string
onChange?(
e:
| FormEvent<HTMLInputElement>
| ChangeEvent<HTMLInputElement>
| ChangeEvent<HTMLSelectElement>
| ChangeEvent<HTMLTextAreaElement>
): void
rows?: number
multiple?: boolean
pattern?: string
min?: string
disabled?: boolean
field?: {
name: string
value: string
onChange: () => void
onBlur: () => void
}
}
export default function Input(props: InputProps) {
const {
required,
name,
label,
help,
additionalComponent,
field
} = props as Partial<InputProps>
return (
<div className={styles.field}>
<Label htmlFor={name} required={required}>
{label}
</Label>
<InputElement {...field} {...props} />
{help && <Help>{help}</Help>}
{additionalComponent && additionalComponent}
</div>
)
}

View File

@ -1,24 +0,0 @@
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: var(--color-primary);
position: fixed;
z-index: 99;
top: 0;
left: 0;
width: 100%;
height: 0.2rem;
}
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px var(--color-primary), 0 0 5px var(--color-primary);
opacity: 1;
transform: rotate(3deg) translate(0px, -4px);
}

View File

@ -1,49 +0,0 @@
import React, { useEffect } from 'react'
import NProgress, { NProgressOptions } from 'nprogress'
import Router from 'next/router'
//
// Component loosely taken from, but highly refactored
// https://github.com/sergiodxa/next-nprogress/blob/master/src/component.js
//
declare type NProgressContainerProps = {
showAfterMs?: number
options?: NProgressOptions
}
export default function NProgressContainer({
showAfterMs = 300,
options
}: NProgressContainerProps) {
let timer: NodeJS.Timeout
function routeChangeStart() {
clearTimeout(timer)
timer = setTimeout(NProgress.start, showAfterMs)
}
function routeChangeEnd() {
clearTimeout(timer)
NProgress.done()
}
useEffect(() => {
if (options) {
NProgress.configure(options)
}
Router.events.on('routeChangeStart', routeChangeStart)
Router.events.on('routeChangeComplete', routeChangeEnd)
Router.events.on('routeChangeError', routeChangeEnd)
return () => {
clearTimeout(timer)
Router.events.off('routeChangeStart', routeChangeStart)
Router.events.off('routeChangeComplete', routeChangeEnd)
Router.events.off('routeChangeError', routeChangeEnd)
}
}, [])
return <div />
}

View File

@ -6,7 +6,6 @@ import {
AdditionalInformationMarket, AdditionalInformationMarket,
MetaDataMarket MetaDataMarket
} from '../../@types/MetaData' } from '../../@types/MetaData'
import { findServiceByType } from '../../utils'
import Tags from '../atoms/Tags' 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'
@ -17,7 +16,7 @@ declare type AssetTeaserProps = {
} }
const AssetTeaser: React.FC<AssetTeaserProps> = ({ ddo }: AssetTeaserProps) => { const AssetTeaser: React.FC<AssetTeaserProps> = ({ ddo }: AssetTeaserProps) => {
const { attributes } = findServiceByType(ddo, 'metadata') const { attributes } = ddo.findServiceByType('metadata')
const { name, price } = attributes.main const { name, price } = attributes.main
let description let description

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { DDO, Ocean } from '@oceanprotocol/squid' import { useNavigate } from '@reach/router'
import { useRouter } from 'next/router' import { DDO } from '@oceanprotocol/squid'
import { findServiceByType, 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'
import { useOcean } from '@oceanprotocol/react' import { useOcean } from '@oceanprotocol/react'
@ -15,19 +15,17 @@ const content = [
export default function DeleteAction({ ddo }: { ddo: DDO }) { export default function DeleteAction({ ddo }: { ddo: DDO }) {
const { ocean, accountId } = useOcean() const { ocean, accountId } = useOcean()
const navigate = useNavigate()
const isOwner = ddo.publicKey[0].owner === accountId const isOwner = ddo.publicKey[0].owner === accountId
const router = useRouter()
const [isModal, setIsModal] = useState(false) const [isModal, setIsModal] = useState(false)
const [status, setStatus] = useState(0) // 0-confirmation, 1-deleting, 2-success, 3-error const [status, setStatus] = useState(0) // 0-confirmation, 1-deleting, 2-success, 3-error
const { attributes } = findServiceByType(ddo, 'metadata') const { attributes } = ddo.findServiceByType('metadata')
useEffect(() => { useEffect(() => {
let tId: number let tId: number
if (status === 2) { if (status === 2) {
tId = window.setTimeout(() => { tId = window.setTimeout(() => {
router.push(`/explore`) navigate(`/`)
}, 1000) }, 1000)
} }
return () => { return () => {

View File

@ -105,7 +105,7 @@ export default function Form({
transformErrors={transformErrors} transformErrors={transformErrors}
> >
<div> <div>
<Button disabled={buttonDisabled} primary> <Button disabled={buttonDisabled} style="primary">
Submit Submit
</Button> </Button>
</div> </div>

View File

@ -1,7 +1,6 @@
import React from 'react' import React from 'react'
import { ComputeItem } from '@oceanprotocol/react' import { ComputeItem } from '@oceanprotocol/react'
import BaseDialog from '../atoms/BaseDialog' import BaseDialog from '../atoms/BaseDialog'
import { findServiceByType } from '../../utils'
import styles from './JobDetailsDialog.module.css' import styles from './JobDetailsDialog.module.css'
import MetaItem from '../templates/AssetDetails/MetaItem' import MetaItem from '../templates/AssetDetails/MetaItem'
import Time from '../atoms/Time' import Time from '../atoms/Time'
@ -19,7 +18,7 @@ export default function JobDetailsDialog({
}) { }) {
if (!computeItem) return null if (!computeItem) return null
const { attributes } = findServiceByType(computeItem.ddo, 'metadata') const { attributes } = computeItem.ddo.findServiceByType('metadata')
const { name } = attributes.main const { name } = attributes.main
const { const {
dateCreated, dateCreated,

View File

@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useNavigate } from '@reach/router'
import Form from '../../molecules/Form/index' import Form from '../../molecules/Form/index'
import { import {
PublishFormSchema, PublishFormSchema,
@ -11,7 +12,6 @@ import { MetaDataMarket } from '../../../@types/MetaData'
import { File, MetaData } from '@oceanprotocol/squid' import { File, MetaData } from '@oceanprotocol/squid'
import { isBrowser, toStringNoMS } from '../../../utils' import { isBrowser, toStringNoMS } from '../../../utils'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { useRouter } from 'next/router'
import styles from './PublishForm.module.css' import styles from './PublishForm.module.css'
import utils from 'web3-utils' import utils from 'web3-utils'
import AssetModel from '../../../models/Asset' import AssetModel from '../../../models/Asset'
@ -21,8 +21,6 @@ import {
ServiceCompute ServiceCompute
} from '@oceanprotocol/squid/dist/node/ddo/Service' } from '@oceanprotocol/squid/dist/node/ddo/Service'
declare type PublishFormProps = {}
const FILES_DATA_LOCAL_STORAGE_KEY = 'filesData' const FILES_DATA_LOCAL_STORAGE_KEY = 'filesData'
const PUBLISH_FORM_LOCAL_STORAGE_KEY = 'publishForm' const PUBLISH_FORM_LOCAL_STORAGE_KEY = 'publishForm'
@ -101,11 +99,11 @@ export function transformPublishFormToMetadata(
return metadata return metadata
} }
const PublishForm: React.FC<PublishFormProps> = () => { const PublishForm: React.FC<any> = () => {
const [buttonDisabled, setButtonDisabled] = useState(false) const [buttonDisabled, setButtonDisabled] = useState(false)
const { web3Connect } = useWeb3() const { web3Connect } = useWeb3()
const { ocean, account } = useOcean() const { ocean, account } = useOcean()
const router = useRouter() const navigate = useNavigate()
const [data, updateData] = useStoredValue( const [data, updateData] = useStoredValue(
PUBLISH_FORM_LOCAL_STORAGE_KEY, PUBLISH_FORM_LOCAL_STORAGE_KEY,
publishFormData publishFormData
@ -172,7 +170,7 @@ const PublishForm: React.FC<PublishFormProps> = () => {
className: styles.success className: styles.success
}) })
toast.dismiss(submittingToast) toast.dismiss(submittingToast)
router.push(`/asset/${asset.id}`) navigate(`/asset/${asset.id}`)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} finally { } finally {

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, ChangeEvent, FormEvent } from 'react' import React, { useState, ChangeEvent, FormEvent } from 'react'
import { useRouter } from 'next/router' import { useNavigate } from '@reach/router'
import styles from './SearchBar.module.css' import styles from './SearchBar.module.css'
import Loader from '../atoms/Loader' import Loader from '../atoms/Loader'
import Button from '../atoms/Button' import Button from '../atoms/Button'
@ -15,7 +15,7 @@ export default function SearchBar({
filters?: boolean filters?: boolean
large?: true large?: true
}) { }) {
const router = useRouter() const navigate = useNavigate()
const [value, setValue] = useState(initialValue || '') const [value, setValue] = useState(initialValue || '')
const [searchStarted, setSearchStarted] = useState(false) const [searchStarted, setSearchStarted] = useState(false)
@ -29,20 +29,9 @@ export default function SearchBar({
if (value === '') return if (value === '') return
setSearchStarted(true) setSearchStarted(true)
router.push(`/search?text=${value}`) navigate(`/search?text=${value}`)
} }
useEffect(() => {
// fix for storybook
if (!router) return
router.events.on('routeChangeComplete', () => setSearchStarted(false))
return () => {
router.events.off('routeChangeComplete', () => setSearchStarted(false))
}
}, [])
return ( return (
<form className={styles.form}> <form className={styles.form}>
<div className={styles.inputGroup}> <div className={styles.inputGroup}>

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { DDO, Ocean, Logger } from '@oceanprotocol/squid' import { DDO, Ocean } from '@oceanprotocol/squid'
import { ServiceMetadata } from '@oceanprotocol/squid/dist/node/ddo/Service' import { ServiceMetadata } from '@oceanprotocol/squid/dist/node/ddo/Service'
import { fromWei } from 'web3-utils' import { fromWei } from 'web3-utils'
import compareAsBN, { Comparisson } from '../../utils/compareAsBN' import compareAsBN, { Comparisson } from '../../utils/compareAsBN'
@ -7,7 +7,6 @@ import Loader from '../atoms/Loader'
import Web3Feedback from '../molecules/Web3Feedback' import Web3Feedback from '../molecules/Web3Feedback'
import Dropzone from '../atoms/Dropzone' import Dropzone from '../atoms/Dropzone'
import Price from '../atoms/Price' import Price from '../atoms/Price'
import { findServiceByType } from '../../utils'
import { import {
computeOptions, computeOptions,
useCompute, useCompute,
@ -42,7 +41,7 @@ export default function Compute({
const [isPublished, setIsPublished] = useState(false) const [isPublished, setIsPublished] = useState(false)
const [file, setFile] = useState(null) const [file, setFile] = useState(null)
const metadata = findServiceByType(ddo, 'metadata') as ServiceMetadata const metadata = ddo.findServiceByType('metadata') as ServiceMetadata
const { price } = metadata.attributes.main const { price } = metadata.attributes.main
const isFree = price === '0' const isFree = price === '0'
@ -118,7 +117,7 @@ export default function Compute({
<div className={styles.jobButtonWrapper}> <div className={styles.jobButtonWrapper}>
<Button <Button
primary style="primary"
onClick={() => startJob()} onClick={() => startJob()}
disabled={isComputeButtonDisabled} disabled={isComputeButtonDisabled}
> >

View File

@ -1,7 +1,6 @@
import React from 'react' import React from 'react'
import { DDO } from '@oceanprotocol/squid' import { DDO } from '@oceanprotocol/squid'
import Web3 from 'web3' import Web3 from 'web3'
import { findServiceByType } from '../../utils'
import compareAsBN, { Comparisson } from '../../utils/compareAsBN' import compareAsBN, { Comparisson } from '../../utils/compareAsBN'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import File from '../atoms/File' import File from '../atoms/File'
@ -18,9 +17,9 @@ export default function Consume({ ddo }: { ddo: DDO | undefined }) {
const { web3 } = useWeb3() const { web3 } = useWeb3()
const { ocean, balanceInOcean } = useOcean() const { ocean, balanceInOcean } = useOcean()
const { consume, consumeStepText, isLoading } = useConsume() const { consume, consumeStepText, isLoading } = useConsume()
const { attributes } = findServiceByType(ddo, 'metadata') const { attributes } = ddo.findServiceByType('metadata')
const { price } = attributes.main const { price } = attributes.main
const file = (attributes as MetaDataMarket).main.files[0] const file = (attributes as any).main.files[0]
const isFree = price === '0' const isFree = price === '0'
const isBalanceSufficient = const isBalanceSufficient =
isFree || isFree ||

View File

@ -11,7 +11,6 @@ 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/squid'
import { findServiceByType } from '../../utils'
const consumedColumns = [ const consumedColumns = [
{ {
@ -57,7 +56,7 @@ export default function ConsumedList() {
if (!consumedItems) return if (!consumedItems) return
const data = consumedItems.map((ddo) => { const data = consumedItems.map((ddo) => {
const { attributes } = findServiceByType(ddo, 'metadata') const { attributes } = ddo.findServiceByType('metadata')
const { name, price, datePublished } = attributes.main as MetaDataMain const { name, price, datePublished } = attributes.main as MetaDataMain
return { return {
published: datePublished, published: datePublished,

View File

@ -1,9 +1,10 @@
import React from 'react' import React from 'react'
import styles from './Footer.module.css' import styles from './Footer.module.css'
import { copyright } from '../../../site.config'
import Markdown from '../atoms/Markdown' import Markdown from '../atoms/Markdown'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
export default function Footer() { export default function Footer() {
const { copyright } = useSiteMetadata()
const year = new Date().getFullYear() const year = new Date().getFullYear()
return ( return (

View File

@ -9,7 +9,6 @@ import {
import Price from '../atoms/Price' import Price from '../atoms/Price'
import { fromWei } from 'web3-utils' import { fromWei } from 'web3-utils'
import { findServiceByType } from '../../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/squid'
@ -97,7 +96,7 @@ export default function JobsList() {
const computeItems = await getComputeItems() const computeItems = await getComputeItems()
if (!computeItems) return if (!computeItems) return
const data = computeItems.map((item) => { const data = computeItems.map((item) => {
const { attributes } = findServiceByType(item.ddo, 'metadata') const { attributes } = item.ddo.findServiceByType('metadata')
const { name, price } = attributes.main as MetaDataMain const { name, price } = attributes.main as MetaDataMain
return { return {
dateCreated: item.job.dateCreated, dateCreated: item.job.dateCreated,
@ -136,7 +135,7 @@ export default function JobsList() {
) : ( ) : (
<> <>
<div> <div>
<Button primary onClick={getJobs}> <Button style="primary" onClick={getJobs}>
Sign to retrieve jobs Sign to retrieve jobs
</Button> </Button>
</div> </div>

View File

@ -9,7 +9,6 @@ import {
import Table from '../atoms/Table' import Table from '../atoms/Table'
import Price from '../atoms/Price' import Price from '../atoms/Price'
import { fromWei } from 'web3-utils' import { fromWei } from 'web3-utils'
import { findServiceByType } from '../../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'
@ -72,7 +71,7 @@ export default function PublishedList() {
}) })
const data = publishedItems.results.map((ddo) => { const data = publishedItems.results.map((ddo) => {
const { attributes } = findServiceByType(ddo, 'metadata') const { attributes } = ddo.findServiceByType('metadata')
const { name, price, datePublished } = attributes.main as MetaDataMain const { name, price, datePublished } = attributes.main as MetaDataMain
return { return {
published: datePublished, published: datePublished,

View File

@ -0,0 +1,51 @@
import React from 'react'
import styles from './History.module.css'
import Web3Feedback from '../molecules/Web3Feedback'
import ConsumedList from '../organisms/ConsumedList'
import PublishedList from '../organisms/PublishedList'
import JobsList from '../organisms/JobsList'
const sections = [
{
title: 'Published',
component: <PublishedList />
},
{
title: 'Downloaded',
component: <ConsumedList />
},
{
title: 'Compute Jobs',
component: <JobsList />
}
]
const Section = ({ title, component }: { title: string; component: any }) => {
return (
<div className={styles.section}>
<h2 className={styles.sectionTitle}>{title}</h2>
{component}
</div>
)
}
const HistoryPage: React.FC = () => {
return (
<article className={styles.grid}>
<div>
{sections.map((section) => {
const { title, component } = section
return <Section key={title} title={title} component={component} />
})}
</div>
<aside>
<div className={styles.sticky}>
<Web3Feedback />
</div>
</aside>
</article>
)
}
export default HistoryPage

View File

@ -1,21 +1,3 @@
.header {
max-width: 37rem;
margin-left: auto;
margin-right: auto;
margin-bottom: calc(var(--spacer) * 3);
margin-top: 3vh;
}
.header h1 {
margin-bottom: calc(var(--spacer) / 4);
}
.header h2 {
font-size: var(--font-size-large);
color: var(--color-secondary);
margin-bottom: calc(var(--spacer) * 2);
}
.actions { .actions {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));

View File

@ -1,14 +1,12 @@
import React from 'react' import React, { ReactElement } from 'react'
import { Link } from 'gatsby' import { Link } from 'gatsby'
import shortid from 'shortid' import shortid from 'shortid'
import Layout from '../../components/Layout'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import SearchBar from '../molecules/SearchBar' import SearchBar from '../molecules/SearchBar'
import Explore from '../../images/explore.svg' import Explore from '../../images/explore.svg'
import Publish from '../../images/publish.svg' import Publish from '../../images/publish.svg'
import DataPool from '../../images/datapool.svg' import DataPool from '../../images/datapool.svg'
import styles from './Home.module.css' import styles from './Home.module.css'
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
const actions = [ const actions = [
{ {
@ -36,16 +34,10 @@ const actions = [
} }
] ]
export default function HomePage() { export default function HomePage(): ReactElement {
const { siteTitle, siteDescription } = useSiteMetadata()
return ( return (
<Layout noPageHeader> <>
<header className={styles.header}>
<h1>{siteTitle}</h1>
<h2>{siteDescription}</h2>
<SearchBar large /> <SearchBar large />
</header>
<div className={styles.actions}> <div className={styles.actions}>
{actions.map((action) => ( {actions.map((action) => (
@ -63,6 +55,6 @@ export default function HomePage() {
</Link> </Link>
))} ))}
</div> </div>
</Layout> </>
) )
} }

View File

@ -1,16 +1,10 @@
import React from 'react' import React from 'react'
import Layout from '../../components/Layout'
import PublishForm from '../molecules/PublishForm/PublishForm' import PublishForm from '../molecules/PublishForm/PublishForm'
import styles from './Publish.module.css' import styles from './Publish.module.css'
import Web3Feedback from '../molecules/Web3Feedback' import Web3Feedback from '../molecules/Web3Feedback'
const title = 'Publish Data'
const description = `Highlight the important features of your data set to make \
it more discoverable and catch the interest of data consumers.`
const PublishPage: React.FC = () => { const PublishPage: React.FC = () => {
return ( return (
<Layout title={title} description={description}>
<article className={styles.grid}> <article className={styles.grid}>
<PublishForm /> <PublishForm />
<aside> <aside>
@ -19,7 +13,6 @@ const PublishPage: React.FC = () => {
</div> </div>
</aside> </aside>
</article> </article>
</Layout>
) )
} }
export default PublishPage export default PublishPage

View File

@ -1,57 +0,0 @@
import React from 'react'
import Layout from '../../components/Layout'
import styles from './Transactions.module.css'
import Web3Feedback from '../molecules/Web3Feedback'
import ConsumedList from '../organisms/ConsumedList'
import PublishedList from '../organisms/PublishedList'
import JobsList from '../organisms/JobsList'
const title = 'Transactions'
const description = 'Find the data sets and jobs that you previously accessed'
const sections = [
{
title: 'Published',
component: <PublishedList />
},
{
title: 'Downloaded',
component: <ConsumedList />
},
{
title: 'Compute Jobs',
component: <JobsList />
}
]
const Section = ({ title, component }: { title: string; component: any }) => {
return (
<div className={styles.section}>
<h2 className={styles.sectionTitle}>{title}</h2>
{component}
</div>
)
}
const TransactionsPage: React.FC = () => {
return (
<Layout title={title} description={description}>
<article className={styles.grid}>
<div>
{sections.map((section) => {
const { title, component } = section
return <Section key={title} title={title} component={component} />
})}
</div>
<aside>
<div className={styles.sticky}>
<Web3Feedback />
</div>
</aside>
</article>
</Layout>
)
}
export default TransactionsPage

View File

@ -1,8 +1,14 @@
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import { Web3Provider, OceanProvider, Config } from '@oceanprotocol/react'
import { config } from '../config/ocean'
import Styles from '../global/Styles' import Styles from '../global/Styles'
const wrapPageElement = ({ element }: { element: ReactElement }) => ( const wrapPageElement = ({ element }: { element: ReactElement }) => (
<Web3Provider>
<OceanProvider config={config as Config}>
<Styles>{element}</Styles> <Styles>{element}</Styles>
</OceanProvider>
</Web3Provider>
) )
export default wrapPageElement export default wrapPageElement

View File

@ -1,5 +1,4 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { useRouter } from 'next/router'
import { setProperty, JSONparse } from '../utils' import { setProperty, JSONparse } from '../utils'
const CATEGORIES_QUERY_PARAM = 'categories' const CATEGORIES_QUERY_PARAM = 'categories'
@ -9,8 +8,6 @@ const useGetCategoriesFromQueryParam = (
allCategories: string[], allCategories: string[],
setSelectedCategories: (categories: string[]) => void setSelectedCategories: (categories: string[]) => void
) => { ) => {
const router = useRouter()
useEffect(() => { useEffect(() => {
if (router.query && router.query.categories) { if (router.query && router.query.categories) {
const parsedCategories = JSONparse<string[]>( const parsedCategories = JSONparse<string[]>(
@ -27,7 +24,6 @@ const useGetCategoriesFromQueryParam = (
} }
export default function useCategoriesQueryParam(allCategories: string[]) { export default function useCategoriesQueryParam(allCategories: string[]) {
const router = useRouter()
const [selectedCategories, setSelectedCategories] = useState<string[]>([]) const [selectedCategories, setSelectedCategories] = useState<string[]>([])
useGetCategoriesFromQueryParam(allCategories, setSelectedCategories) useGetCategoriesFromQueryParam(allCategories, setSelectedCategories)

View File

@ -1,27 +1,15 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { NextRouter, useRouter } from 'next/router' import { useLocation } from '@reach/router'
import { useDebouncedCallback } from 'use-debounce'
import { setProperty } from '../utils'
function updateQuery(router: NextRouter) {
return (min?: string, max?: string) => {
const query = Object.assign({}, router.query)
setProperty(query, 'minPrice', min)
setProperty(query, 'maxPrice', max)
router.push({
pathname: router.pathname,
query: query
})
}
}
export default function usePriceQueryParams() { export default function usePriceQueryParams() {
const router = useRouter() const location = useLocation()
const [min, setMin] = useState((router.query?.minPrice as string) || '0')
const [max, setMax] = useState((router.query?.maxPrice as string) || '0') const [min, setMin] = useState(
const [debouncedUpdateQuery] = useDebouncedCallback(updateQuery(router), 1000) (JSON.parse(location.search).minPrice as string) || '0'
)
const [max, setMax] = useState(
(JSON.parse(location.search).maxPrice as string) || '0'
)
useEffect(() => { useEffect(() => {
if (parseFloat(max) < parseFloat(min)) { if (parseFloat(max) < parseFloat(min)) {
@ -35,9 +23,5 @@ export default function usePriceQueryParams() {
} }
}, [max]) }, [max])
useEffect(() => {
debouncedUpdateQuery(min, max)
}, [min, max])
return { min, setMin, max, setMax } return { min, setMin, max, setMax }
} }

View File

@ -9,30 +9,11 @@ const query = graphql`
site { site {
siteTitle siteTitle
siteTagline siteTagline
siteDescription
siteUrl siteUrl
siteIcon copyright
siteImage { menu {
childImageSharp {
original {
src
}
}
}
analyticsId
company {
name name
address { link
location
street
city
zip
country
}
}
social {
name
url
} }
} }
} }

View File

@ -1,66 +0,0 @@
import React from 'react'
import { AppProps } from 'next/app'
import Head from 'next/head'
import { DefaultSeo } from 'next-seo'
import { useRouter } from 'next/router'
import NProgress from '../components/atoms/NProgress'
import { title, description, url } from '../../site.config'
import { toast } from 'react-toastify'
// this is the place to import global css
import 'tippy.js/dist/tippy.css'
import 'react-responsive-modal/styles.css'
import '@oceanprotocol/typographies/css/ocean-typo.css'
import 'react-toastify/dist/ReactToastify.css'
import '../styles/global.css'
import '../components/atoms/NProgress.css'
import { Web3Provider, OceanProvider, Config } from '@oceanprotocol/react'
import { config } from '../config/ocean'
export default function marketApp({ Component, pageProps }: AppProps) {
const { asPath } = useRouter()
toast.configure()
// Hacky workaround for mode: pure css modules disallowing any
// css modules with :global scope. We load this as global css ideally
// only when date picker is present which rpesently is only on the
// /publish route.
if (asPath.includes('/publish')) {
require('../styles/datepicker.css')
}
return (
<>
<Head>
<link
href="https://fonts.googleapis.com/css?family=Montserrat:400,400i,600&display=swap"
rel="stylesheet"
key="google-fonts-montserrat"
/>
</Head>
<DefaultSeo
title={title}
description={description}
canonical={`${url}${asPath}`}
openGraph={{
type: 'website',
locale: 'en_US',
images: [{ url: `${url}/share.png` }],
// eslint-disable-next-line @typescript-eslint/camelcase
site_name: title
}}
twitter={{
handle: '@oceanprotocol',
site: '@oceanprotocol',
cardType: 'summary_large_image'
}}
/>
<NProgress />
<Web3Provider>
<OceanProvider config={config as Config}>
<Component {...pageProps} />
</OceanProvider>
</Web3Provider>
</>
)
}

View File

@ -1,117 +0,0 @@
import React from 'react'
import { NextPage, GetStaticProps, GetStaticPaths } from 'next'
import { useRouter } from 'next/router'
import axios from 'axios'
import { Aquarius, Logger } from '@oceanprotocol/squid'
import AssetDetailsPage, {
AssetDetailsPageProps
} from '../../components/templates/AssetDetails'
import { config } from '../../config/ocean'
import { findServiceByType, isDid } from '../../utils'
import Loader from '../../components/atoms/Loader'
import Layout from '../../Layout'
import styles from '../../components/templates/AssetDetails/index.module.css'
const AssetDetails: NextPage<AssetDetailsPageProps> = (
props: AssetDetailsPageProps
) => {
const router = useRouter()
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return (
<Layout title={props.title}>
<div className={styles.loaderWrap}>
<Loader />
</div>
</Layout>
)
}
// parse back the DDO object, workaround for Next.js
let ddo
if (props.ddo) {
ddo = JSON.parse(props.ddo as any)
}
return (
<AssetDetailsPage
title={props.title}
ddo={ddo}
attributes={props.attributes}
error={props.error}
/>
)
}
export function normalizeDid(did: string): string {
if (did.length === 73) {
if (did.charAt(9) === '0' && did.charAt(10) === '0') {
return did.substr(0, 9) + did.substr(11)
}
}
return did
}
export async function getMetadata(did?: string) {
if (did) {
did = normalizeDid(did)
if (!isDid(did)) {
return {
title: 'Not a DID',
error:
'The provided DID in the URL is not a valid DID. Please check your URL.'
}
}
} else {
return {
title: 'No DID provided',
error: 'No DID provided. Please check your URL.'
}
}
let ddo
try {
const aquarius = new Aquarius(config.aquariusUri as string, Logger)
ddo = await aquarius.retrieveDDO(did as string)
if (!ddo) {
return {
title: 'Could not retrieve asset',
error: 'The DDO was not found in Aquarius.'
}
}
} catch (error) {
return { title: 'Error retrieving asset', error: error.message }
}
const { attributes } = findServiceByType(ddo, 'metadata')
const title = attributes.main.name
// stringify the DDO object, workaround for Next.js
return { ddo: JSON.stringify(ddo), attributes, title }
}
export const getStaticProps: GetStaticProps = async ({
params
}: {
params?: { did?: string }
}) => {
if (!params) return { props: { title: 'Not found' } }
const props = await getMetadata(params.did)
return { props }
}
export const getStaticPaths: GetStaticPaths = async () => {
const response = await axios(`${config.aquariusUri}/api/v1/aquarius/assets`)
const assets = response.data.ids
const paths = assets.map((did: string) => ({
params: { did }
}))
return { paths, fallback: true }
}
export default AssetDetails

View File

@ -1,29 +0,0 @@
import React from 'react'
import { NextPage, GetServerSideProps } from 'next'
import { Aquarius, Logger } from '@oceanprotocol/squid'
import { SearchQuery } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import ExplorePage from '../components/pages/Explore'
import { config } from '../config/ocean'
const Explore: NextPage<{ queryResult: string }> = ({ queryResult }) => (
<ExplorePage queryResult={JSON.parse(queryResult)} />
)
export const getServerSideProps: GetServerSideProps = async (context) => {
const searchQuery = {
offset: 15,
page: Number(context.query.page) || 1,
query: {},
sort: {
created: -1
}
} as SearchQuery
const aquarius = new Aquarius(config.aquariusUri as string, Logger)
const queryResult = await aquarius.queryMetadata(searchQuery)
// Note: stringifying the results cause Next.js otherwise complains about
// not being able to serialize the results array for whatever reason.
// So manually serialize them here, and parse them back in the above page.
return { props: { queryResult: JSON.stringify(queryResult) } }
}
export default Explore

30
src/pages/history.tsx Normal file
View File

@ -0,0 +1,30 @@
import React, { ReactElement } from 'react'
import PageHistory from '../components/pages/History'
import Layout from '../components/Layout'
import { graphql, PageProps } from 'gatsby'
export default function PageGatsbyHistory(props: PageProps): ReactElement {
const content = (props.data as any).content.edges[0].node.childPagesJson
const { title, description } = content
return (
<Layout title={title} description={description} location={props.location}>
<PageHistory />
</Layout>
)
}
export const contentQuery = graphql`
query HistoryPageQuery {
content: allFile(filter: { relativePath: { eq: "pages/history.json" } }) {
edges {
node {
childPagesJson {
title
description
}
}
}
}
}
`

View File

@ -1,7 +1,19 @@
import React from 'react' import React, { ReactElement } from 'react'
import { NextPage } from 'next' import { PageProps } from 'gatsby'
import HomePage from '../components/pages/Home' import PageHome from '../components/pages/Home'
import { useSiteMetadata } from '../hooks/useSiteMetadata'
import Layout from '../components/Layout'
const Home: NextPage<{}> = () => <HomePage /> export default function PageGatsbyHome(props: PageProps): ReactElement {
const { siteTitle, siteTagline } = useSiteMetadata()
export default Home return (
<Layout
title={siteTitle}
description={siteTagline}
location={props.location}
>
<PageHome />
</Layout>
)
}

View File

@ -1,9 +1,30 @@
import { NextPage } from 'next' import React, { ReactElement } from 'react'
import React from 'react' import PagePublish from '../components/pages/Publish'
import PublishPage from '../components/pages/Publish' import Layout from '../components/Layout'
import { graphql, PageProps } from 'gatsby'
const Publish: NextPage = () => { export default function PageGatsbyPublish(props: PageProps): ReactElement {
return <PublishPage /> const content = (props.data as any).content.edges[0].node.childPagesJson
const { title, description } = content
return (
<Layout title={title} description={description} location={props.location}>
<PagePublish />
</Layout>
)
} }
export default Publish export const contentQuery = graphql`
query PublishPageQuery {
content: allFile(filter: { relativePath: { eq: "pages/publish.json" } }) {
edges {
node {
childPagesJson {
title
description
}
}
}
}
}
`

View File

@ -1,71 +1,77 @@
import React from 'react' import React, { ReactElement } from 'react'
import { NextPage } from 'next'
import { Aquarius, Logger } from '@oceanprotocol/squid' import { Aquarius, Logger } from '@oceanprotocol/squid'
import { SearchQuery } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius' import { SearchQuery } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import SearchPage, { SearchPageProps } from '../components/templates/Search' import PageSearch, { SearchPageProps } from '../components/templates/Search'
import { config } from '../config/ocean' import { config } from '../config/ocean'
import { JSONparse, priceQueryParamToWei } from '../utils' import { JSONparse, priceQueryParamToWei } from '../utils'
import { PageProps } from 'gatsby'
import Layout from '../components/Layout'
const Search: NextPage<SearchPageProps> = ({ text, tag, queryResult }) => { export default function PageGatsbySearch(props: PageProps): ReactElement {
return <SearchPage text={text} tag={tag} queryResult={queryResult} /> const content = (props.data as any).content.edges[0].node.childPagesJson
const { title, description } = content
return (
<Layout title={title} description={description} location={props.location}>
<PageSearch text={text} tag={tag} queryResult={queryResult} />
</Layout>
)
} }
export function getSearchQuery( // export function getSearchQuery(
page?: string | string[], // page?: string | string[],
offset?: string | string[], // offset?: string | string[],
text?: string | string[], // text?: string | string[],
tag?: string | string[], // tag?: string | string[],
priceQuery?: [string | undefined, string | undefined] // priceQuery?: [string | undefined, string | undefined]
) { // ) {
return { // return {
page: Number(page) || 1, // page: Number(page) || 1,
offset: Number(offset) || 20, // offset: Number(offset) || 20,
query: { // query: {
text, // text,
tags: tag ? [tag] : undefined, // tags: tag ? [tag] : undefined,
price: priceQuery // price: priceQuery
}, // },
sort: { // sort: {
created: -1 // created: -1
} // }
// Something in squid-js is weird when using 'categories: [type]' // // Something in squid-js is weird when using 'categories: [type]'
// which is the only way the query actually returns desired results. // // which is the only way the query actually returns desired results.
// But it doesn't follow 'SearchQuery' interface so we have to assign // // But it doesn't follow 'SearchQuery' interface so we have to assign
// it here. // // it here.
} as SearchQuery // } as SearchQuery
} // }
Search.getInitialProps = async (context) => { // Search.getInitialProps = async (context) => {
const { text, tag, page, offset, minPrice, maxPrice } = context.query // const { text, tag, page, offset, minPrice, maxPrice } = context.query
const minPriceParsed = priceQueryParamToWei( // const minPriceParsed = priceQueryParamToWei(
minPrice as string, // minPrice as string,
'Error parsing context.query.minPrice' // 'Error parsing context.query.minPrice'
) // )
const maxPriceParsed = priceQueryParamToWei( // const maxPriceParsed = priceQueryParamToWei(
maxPrice as string, // maxPrice as string,
'Error parsing context.query.maxPrice' // 'Error parsing context.query.maxPrice'
) // )
const priceQuery = // const priceQuery =
minPriceParsed || maxPriceParsed // minPriceParsed || maxPriceParsed
? // sometimes TS gets a bit silly // ? // sometimes TS gets a bit silly
([minPriceParsed, maxPriceParsed] as [ // ([minPriceParsed, maxPriceParsed] as [
string | undefined, // string | undefined,
string | undefined // string | undefined
]) // ])
: undefined // : undefined
const aquarius = new Aquarius(config.aquariusUri as string, Logger) // const aquarius = new Aquarius(config.aquariusUri as string, Logger)
const queryResult = await aquarius.queryMetadata( // const queryResult = await aquarius.queryMetadata(
getSearchQuery(page, offset, text, tag, priceQuery) // getSearchQuery(page, offset, text, tag, priceQuery)
) // )
return { // return {
text: text, // text: text,
tag: tag, // tag: tag,
queryResult // queryResult
} // }
} // }
export default Search

View File

@ -1,9 +0,0 @@
import { NextPage } from 'next'
import React from 'react'
import TransactionsPage from '../components/pages/Transactions'
const Transactions: NextPage = () => {
return <TransactionsPage />
}
export default Transactions

View File

@ -1,25 +1,9 @@
import axios, { AxiosResponse } from 'axios' import axios, { AxiosResponse } from 'axios'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { File, DDO } from '@oceanprotocol/squid' import { File } from '@oceanprotocol/squid'
import numeral from 'numeral' import numeral from 'numeral'
import {
ServiceAccess,
ServiceCommon,
ServiceType,
ServiceMetadata,
ServiceAuthorization
} from '@oceanprotocol/squid/dist/node/ddo/Service'
import web3Utils from 'web3-utils' import web3Utils from 'web3-utils'
// Helper to work around Next.js serialization of props
// for replacing usage of ddo.findServiceByType()
export function findServiceByType(
ddo: string | Partial<DDO>,
type: ServiceType
): ServiceCommon | ServiceAccess | ServiceMetadata | ServiceAuthorization {
return new DDO(ddo as Partial<DDO>).findServiceByType(type)
}
export function updateQueryStringParameter( export function updateQueryStringParameter(
uri: string, uri: string,
key: string, key: string,

View File

@ -24,10 +24,8 @@ const oceanMock = {
return { return {
results: [ddo] as any[], results: [ddo] as any[],
page: 1, page: 1,
/* eslint-disable @typescript-eslint/camelcase */
total_pages: 1611, total_pages: 1611,
total_results: 1611 total_results: 1611
/* eslint-enable @typescript-eslint/camelcase */
} }
}, },
resolve: () => null as any, resolve: () => null as any,

View File

@ -3,13 +3,11 @@ module.exports = {
transform: { transform: {
'^.+\\.[jt]sx?$': ['babel-jest', { configFile: './jest/babel.config.js' }] '^.+\\.[jt]sx?$': ['babel-jest', { configFile: './jest/babel.config.js' }]
}, },
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node', 'md'],
moduleNameMapper: { moduleNameMapper: {
'.+\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy', '.+\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy',
'.+\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|md)$': '.+\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/tests/unit/__mocks__/fileMock.js', '<rootDir>/tests/unit/__mocks__/file-mock.js',
'\\.svg': '<rootDir>/tests/unit/__mocks__/svgrMock.js', '\\.svg': '<rootDir>/tests/unit/__mocks__/svgr-mock.js'
'next/router': '<rootDir>/tests/unit/__mocks__/nextRouter.js'
}, },
testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'], testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'],
transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'], transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'],

View File

@ -1,77 +0,0 @@
import React from 'react'
import { render } from '@testing-library/react'
import AssetDetails, { getMetadata } from '../../../src/pages/asset/[did]'
import ddo from '../__fixtures__/ddo'
import { findServiceByType } from '../../../src/utils'
import { MetaDataMarket } from '../../../src/@types/MetaData'
import oceanMock from '../__mocks__/ocean-mock'
import web3ProviderMock from '../__mocks__/web3'
const { attributes } = findServiceByType(ddo, 'metadata')
// import { useOcean } from '@oceanprotocol/react'
jest.mock('web3')
jest.mock('@oceanprotocol/react')
// eslint-disable-next-line
jest.mock('@oceanprotocol/react', () => ({
useOcean: () => {
return {
ocean: oceanMock
}
},
useWeb3: () => {
return {
web3: web3ProviderMock,
account: '0x0000000011111111aaaaaaaabbbbbbbb22222222',
ethProviderStatus: 1
}
},
useConsume: () => {
return {
consume: () => null as any,
consumeStepText: '',
isLoading: false
}
},
useMetadata: () => {
return {
getCuration: () => {
return Promise.resolve({ rating: 0, numVotes: 0 })
}
}
}
}))
describe('AssetDetails', () => {
it('renders without crashing', () => {
const { container } = render(
<AssetDetails
ddo={JSON.stringify(ddo) as any}
attributes={attributes as MetaDataMarket}
title="Hello"
/>
)
expect(container.firstChild).toBeInTheDocument()
})
})
describe('getMetadata()', () => {
it('not a valid DID', async () => {
const response = await getMetadata('hello')
expect(response.title).toBe('Not a DID')
})
it('Not Found', async () => {
const response = await getMetadata(
'did:op:c678e7e5963d4fdc99afea49ac221d4d4177790f30204417823319d4d35f851f'
)
expect(response.title).toBe('Could not retrieve asset')
})
it('Found', async () => {
const response = await getMetadata(
'did:op:8898adb69e334755a568738ce3f6c03760f9eb5a4f344c688e483a04cb0855d6'
)
expect(response.title).toBe('compute1')
})
})

View File

@ -1,28 +0,0 @@
import React from 'react'
import { render } from '@testing-library/react'
import Explore, { getServerSideProps } from '../../../src/pages/explore'
import ddo from '../__fixtures__/ddo'
import { DDO } from '@oceanprotocol/squid'
const asset = new DDO(ddo)
const queryResult = {
results: [asset, asset, asset, asset, asset, asset].map(
(asset) => new DDO(asset)
),
page: 1,
totalPages: 100,
totalResults: 1000
}
describe('Explore', () => {
it('renders without crashing', () => {
const { container } = render(
<Explore queryResult={JSON.stringify(queryResult)} />
)
expect(container.firstChild).toBeInTheDocument()
})
it('getServerSideProps', () => {
getServerSideProps({ query: { page: '1' } } as any)
})
})

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import { render } from '@testing-library/react' import { render } from '@testing-library/react'
import Publish from '../../../src/pages/publish' import Publish from '../../../src/pages/publish'
import web3ProviderMock, { context } from '../__mocks__/web3provider' import web3ProviderMock from '../__mocks__/web3provider'
import oceanMock from '../__mocks__/ocean-mock' import oceanMock from '../__mocks__/ocean-mock'
// eslint-disable-next-line // eslint-disable-next-line