1
0
mirror of https://github.com/oceanprotocol/commons.git synced 2023-03-15 18:03:00 +01:00

init network switcher component

This commit is contained in:
Max Berman 2019-12-16 11:44:14 +01:00
parent 70a3339f8b
commit 8023e30061
10 changed files with 423 additions and 374 deletions

View File

@ -1,43 +1,49 @@
{ {
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"project": [ "project": [
"./tsconfig.json", "./tsconfig.json",
"./client/tsconfig.json", "./client/tsconfig.json",
"./server/tsconfig.json", "./server/tsconfig.json",
"./cypress/tsconfig.json" "./cypress/tsconfig.json"
] ]
}, },
"extends": [ "extends": [
"oceanprotocol", "oceanprotocol",
"oceanprotocol/react", "oceanprotocol/react",
"plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended", "plugin:prettier/recommended",
"prettier/react", "prettier/react",
"prettier/standard", "prettier/standard",
"prettier/@typescript-eslint", "prettier/@typescript-eslint",
"plugin:cypress/recommended" "plugin:cypress/recommended"
],
"plugins": ["@typescript-eslint", "prettier", "cypress"],
"rules": {
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/member-delimiter-style": [
"error",
{ "multiline": { "delimiter": "none" } }
], ],
"plugins": ["@typescript-eslint", "prettier", "cypress"], "@typescript-eslint/no-explicit-any": "off",
"rules": { "react/no-unescaped-entities": 0,
"@typescript-eslint/explicit-function-return-type": 0, "react/self-closing-comp": 0,
"@typescript-eslint/member-delimiter-style": [ "react/void-dom-elements-no-children": 0,
"error", "react/jsx-indent": 0,
{ "multiline": { "delimiter": "none" } } "react/jsx-indent-props": 0,
], "react/jsx-max-props-per-line": 0
"@typescript-eslint/no-explicit-any": "off" },
}, "env": {
"env": { "es6": true,
"es6": true, "browser": true,
"browser": true, "node": true,
"node": true, "jest": true,
"jest": true, "cypress/globals": true
"cypress/globals": true },
}, "settings": {
"settings": { "react": {
"react": { "version": "16.10"
"version": "16.10"
}
} }
}
} }

View File

@ -1,5 +1,6 @@
{ {
"semi": false, "semi": false,
"singleQuote": true, "singleQuote": true,
"trailingComma": "none" "trailingComma": "none",
} "tabWidth": 2
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@ -5,33 +5,36 @@ import Footer from './components/organisms/Footer'
import Spinner from './components/atoms/Spinner' import Spinner from './components/atoms/Spinner'
import { User } from './context' import { User } from './context'
import Routes from './Routes' import Routes from './Routes'
import {commonsNetwork} from './components/molecules/NetworkSwitcher'
import './styles/global.scss' import './styles/global.scss'
import styles from './App.module.scss' import styles from './App.module.scss'
console.log(commonsNetwork)
export default class App extends Component { export default class App extends Component {
public render() { public render() {
return ( return (
<div className={styles.app}> <div className={styles.app}>
<Router> <Router>
<> <>
<Header /> <Header />
<main className={styles.main}> <main className={styles.main}>
{this.context.isLoading ? ( {this.context.isLoading ? (
<div className={styles.loader}> <div className={styles.loader}>
<Spinner message={this.context.message} /> <Spinner message={this.context.message} />
</div> </div>
) : ( ) : (
<Routes /> <Routes />
)} )}
</main> </main>
<Footer />
<Footer /> </>
</> </Router>
</Router> </div>
</div> )
) }
}
} }
App.contextType = User App.contextType = User

View File

@ -0,0 +1,12 @@
import { CONNECTIONS } from '../../config'
/* TEMP NETWORK SWITCHER */
const urlParams = new URLSearchParams(window.location.search)
const network = urlParams.get('network') || 'pacific'
const idx = Object.keys(CONNECTIONS).indexOf(network)
const commonsNetwork = Object.values(CONNECTIONS)[idx]
export { commonsNetwork }

View File

@ -3,110 +3,109 @@ import { VersionNumbersState } from '.'
import VersionTableRow from './VersionTableRow' import VersionTableRow from './VersionTableRow'
import styles from './VersionTable.module.scss' import styles from './VersionTable.module.scss'
import VersionNumber from './VersionNumber' import VersionNumber from './VersionNumber'
import { useParams } from 'react-router-dom'
import { import {
serviceUri, serviceUri,
nodeUri, nodeUri,
aquariusUri, aquariusUri,
brizoUri, brizoUri,
brizoAddress, brizoAddress,
secretStoreUri, secretStoreUri,
faucetUri faucetUri,
CONNECTIONS
} from '../../../config' } from '../../../config'
const commonsConfig = { const commonsConfig = {
serviceUri, serviceUri,
nodeUri, nodeUri,
aquariusUri, aquariusUri,
brizoUri, brizoUri,
brizoAddress, brizoAddress,
secretStoreUri, secretStoreUri,
faucetUri faucetUri
} }
export const VersionTableContracts = ({ export const VersionTableContracts = ({
contracts, contracts,
network, network,
keeperVersion keeperVersion
}: { }: {
contracts: { contracts: {
[contractName: string]: string [contractName: string]: string
} }
network: string network: string
keeperVersion?: string keeperVersion?: string
}) => ( }) => (
<table> <table>
<tbody> <tbody>
<tr> <tr>
<td> <td>
<strong>Keeper Contracts</strong> <strong>Keeper Contracts</strong>
</td> </td>
<td> <td>
<VersionNumber <VersionNumber name="Keeper Contracts" version={keeperVersion} />
name="Keeper Contracts" </td>
version={keeperVersion} </tr>
/> {contracts &&
</td> Object.keys(contracts)
</tr> // sort alphabetically
{contracts && .sort((a, b) => a.localeCompare(b))
Object.keys(contracts) .map(key => {
// sort alphabetically const submarineLink = `https://submarine.${
.sort((a, b) => a.localeCompare(b)) network === 'pacific' ? 'oceanprotocol' : `${network}.dev-ocean`
.map(key => { }.com/address/${contracts[key]}`
const submarineLink = `https://submarine.${
network === 'pacific'
? 'oceanprotocol'
: `${network}.dev-ocean`
}.com/address/${contracts[key]}`
return ( return (
<tr key={key}>
<td> <tr key={key}>
<code className={styles.label}>{key}</code> <td>
</td> <code className={styles.label}>{key}</code>
<td> </td>
<a href={submarineLink}> <td>
<code>{contracts[key]}</code> <a href={submarineLink}>
</a> <code>{contracts[key]}</code>
</td> </a>
</tr> </td>
) </tr>
})} )
</tbody> })}
</table> </tbody>
</table>
) )
export const VersionTableCommons = () => ( export const VersionTableCommons = () => (
<table> <table>
<tbody> <tbody>
{Object.entries(commonsConfig).map(([key, value]) => ( {Object.entries(commonsConfig).map(([key, value]) => (
<tr key={key}> <tr key={key}>
<td> <td>
<code className={styles.label}>{key}</code> <code className={styles.label}>{key}</code>
</td> </td>
<td> <td>
<code>{value}</code> <code>{value}</code>
</td> </td>
</tr> </tr>
))} ))}
</tbody> </tbody>
</table> </table>
) )
const VersionTable = ({ data }: { data: VersionNumbersState }) => { const VersionTable = ({ data }: { data: VersionNumbersState }) => {
return ( return (
<div className={styles.tableWrap}> <div className={styles.tableWrap}>
<table className={styles.table}> <table className={styles.table}>
<tbody> <tbody>
{Object.entries(data) {Object.entries(data)
.filter(([key]) => key !== 'status') .filter(([key]) => key !== 'status')
.map(([key, value]) => ( .map(([key, value]) => (
<VersionTableRow key={key} value={value} /> <VersionTableRow key={key} value={value} />
))} ))}
</tbody> </tbody>
</table> </table>
</div> </div>
) )
} }
export default VersionTable export default VersionTable

View File

@ -2,26 +2,26 @@
// commons-server connection // commons-server connection
// //
export const serviceUri = export const serviceUri =
process.env.REACT_APP_SERVICE_URI || 'http://localhost:4000' process.env.REACT_APP_SERVICE_URI || 'http://localhost:4000'
// //
// OCEAN REMOTE CONNECTIONS // OCEAN REMOTE CONNECTIONS
// //
export const nodeUri = export const nodeUri =
process.env.REACT_APP_NODE_URI || 'https://pacific.oceanprotocol.com' process.env.REACT_APP_NODE_URI || 'https://pacific.oceanprotocol.com'
export const aquariusUri = export const aquariusUri =
process.env.REACT_APP_AQUARIUS_URI || process.env.REACT_APP_AQUARIUS_URI ||
'https://aquarius.commons.oceanprotocol.com' 'https://aquarius.commons.oceanprotocol.com'
export const brizoUri = export const brizoUri =
process.env.REACT_APP_BRIZO_URI || 'https://brizo.commons.oceanprotocol.com' process.env.REACT_APP_BRIZO_URI || 'https://brizo.commons.oceanprotocol.com'
export const brizoAddress = export const brizoAddress =
process.env.REACT_APP_BRIZO_ADDRESS || process.env.REACT_APP_BRIZO_ADDRESS ||
'0x008c25ed3594e094db4592f4115d5fa74c4f41ea' '0x008c25ed3594e094db4592f4115d5fa74c4f41ea'
export const secretStoreUri = export const secretStoreUri =
process.env.REACT_APP_SECRET_STORE_URI || process.env.REACT_APP_SECRET_STORE_URI ||
'https://secret-store.oceanprotocol.com' 'https://secret-store.oceanprotocol.com'
export const faucetUri = export const faucetUri =
process.env.REACT_APP_FAUCET_URI || 'https://faucet.oceanprotocol.com' process.env.REACT_APP_FAUCET_URI || 'https://faucet.oceanprotocol.com'
// //
// APP CONFIG // APP CONFIG
@ -30,13 +30,40 @@ export const verbose = true
export const analyticsId = 'UA-60614729-11' export const analyticsId = 'UA-60614729-11'
export const showChannels = export const showChannels =
process.env.REACT_APP_SHOW_CHANNELS === 'true' || false process.env.REACT_APP_SHOW_CHANNELS === 'true' || false
export const allowPricing = export const allowPricing =
process.env.REACT_APP_ALLOW_PRICING === 'true' || false process.env.REACT_APP_ALLOW_PRICING === 'true' || false
export const showRequestTokens = export const showRequestTokens =
process.env.REACT_APP_SHOW_REQUEST_TOKENS_BUTTON === 'true' || false process.env.REACT_APP_SHOW_REQUEST_TOKENS_BUTTON === 'true' || false
// https://ipfs.github.io/public-gateway-checker/ // https://ipfs.github.io/public-gateway-checker/
export const ipfsGatewayUri = export const ipfsGatewayUri =
process.env.REACT_APP_IPFS_GATEWAY_URI || 'https://gateway.ipfs.io' process.env.REACT_APP_IPFS_GATEWAY_URI || 'https://gateway.ipfs.io'
export const ipfsNodeUri = export const ipfsNodeUri =
process.env.REACT_APP_IPFS_NODE_URI || 'https://ipfs.infura.io:5001' process.env.REACT_APP_IPFS_NODE_URI || 'https://ipfs.infura.io:5001'
export const CONNECTIONS = {
pacific: {
nodeUri: 'https://pacific.oceanprotocol.com',
aquariusUri: 'https://aquarius.commons.oceanprotocol.com',
brizoUri: 'https://brizo.commons.oceanprotocol.com',
brizoAddress: '0x008c25ed3594e094db4592f4115d5fa74c4f41ea',
secretStoreUri: 'https://secret-store.oceanprotocol.com',
faucetUri: 'https://faucet.oceanprotocol.com'
},
nile: {
nodeUri: 'https://nile.dev-ocean.com',
aquariusUri: 'https://aquarius.nile.dev-ocean.com',
brizoUri: 'https://brizo.nile.dev-ocean.com',
brizoAddress: '0x4aaab179035dc57b35e2ce066919048686f82972',
secretStoreUri: 'https://secret-store.nile.dev-ocean.com',
faucetUri: 'https://faucet.nile.dev-ocean.com'
},
duero: {
nodeUri: 'https://duero.dev-ocean.com',
aquariusUri: 'https://aquarius.duero.dev-ocean.com',
brizoUri: 'https://brizo.duero.dev-ocean.com',
brizoAddress: '0x9d4ed58293f71122ad6a733c1603927a150735d0',
secretStoreUri: 'https://secret-store.duero.dev-ocean.com',
faucetUri: 'https://faucet.duero.dev-ocean.com'
}
}

View File

@ -21,6 +21,7 @@ export { renderToDOM }
renderToDOM() renderToDOM()
// If you want your app to work offline and load faster, you can change // If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls. // unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA // Learn more about service workers: http://bit.ly/CRA-PWA

View File

@ -1,55 +1,58 @@
import { Ocean, Logger } from '@oceanprotocol/squid' import { Ocean, Logger } from '@oceanprotocol/squid'
import Web3 from 'web3' import Web3 from 'web3'
import { User } from './context'
import { import {
aquariusUri,
brizoUri,
brizoAddress,
faucetUri,
nodeUri,
secretStoreUri,
verbose
} from './config'
export async function provideOcean(web3Provider: Web3) {
const config = {
web3Provider,
nodeUri,
aquariusUri, aquariusUri,
brizoUri, brizoUri,
brizoAddress, brizoAddress,
faucetUri,
nodeUri,
secretStoreUri, secretStoreUri,
verbose verbose
} from './config' }
const ocean: any = await Ocean.getInstance(config)
export async function provideOcean(web3Provider: Web3) { return { ocean }
const config = {
web3Provider,
nodeUri,
aquariusUri,
brizoUri,
brizoAddress,
secretStoreUri,
verbose
}
const ocean: any = await Ocean.getInstance(config)
return { ocean }
} }
// //
// Faucet // Faucet
// //
export interface FaucetResponse { export interface FaucetResponse {
success: boolean success: boolean
message: string message: string
trxHash?: string trxHash?: string
} }
export async function requestFromFaucet(account: string) { export async function requestFromFaucet(account: string) {
try { try {
const url = `${faucetUri}/faucet` const url = `${faucetUri}/faucet`
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
address: account, address: account,
agent: 'commons' agent: 'commons'
}) })
}) })
return response.json() return response.json()
} catch (error) { } catch (error) {
Logger.error('requestFromFaucet', error.message) Logger.error('requestFromFaucet', error.message)
} }
} }

View File

@ -1,9 +1,9 @@
import React, { import React, {
lazy, lazy,
Suspense, Suspense,
FormEvent, FormEvent,
PureComponent, PureComponent,
ChangeEvent ChangeEvent
} from 'react' } from 'react'
import axios from 'axios' import axios from 'axios'
import { Logger, File } from '@oceanprotocol/squid' import { Logger, File } from '@oceanprotocol/squid'
@ -21,202 +21,196 @@ import Spinner from '../../../components/atoms/Spinner'
const Ipfs = lazy(() => import('./Ipfs')) const Ipfs = lazy(() => import('./Ipfs'))
export interface FilePublish extends File { export interface FilePublish extends File {
found: boolean // non-standard found: boolean // non-standard
} }
interface FilesProps { interface FilesProps {
files: File[] files: File[]
placeholder: string placeholder: string
help?: string help?: string
name: string name: string
onChange( onChange(
event: event:
| ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLInputElement>
| FormEvent<HTMLInputElement> | FormEvent<HTMLInputElement>
| ChangeEvent<HTMLSelectElement> | ChangeEvent<HTMLSelectElement>
| ChangeEvent<HTMLTextAreaElement> | ChangeEvent<HTMLTextAreaElement>
): void ): void
} }
interface FilesStates { interface FilesStates {
isFormShown: boolean isFormShown: boolean
isIpfsFormShown: boolean isIpfsFormShown: boolean
} }
const buttons = [ const buttons = [
{ {
id: 'url', id: 'url',
title: '+ From URL', title: '+ From URL',
titleActive: '- Cancel' titleActive: '- Cancel'
}, },
{ {
id: 'ipfs', id: 'ipfs',
title: '+ Add to IPFS', title: '+ Add to IPFS',
titleActive: '- Cancel' titleActive: '- Cancel'
} }
] ]
export default class Files extends PureComponent<FilesProps, FilesStates> { export default class Files extends PureComponent<FilesProps, FilesStates> {
public state: FilesStates = { public state: FilesStates = {
isFormShown: false,
isIpfsFormShown: false
}
// for canceling axios requests
public signal = axios.CancelToken.source()
public componentWillUnmount() {
this.signal.cancel()
}
private toggleForm = (e: Event, form: string) => {
e.preventDefault()
this.setState({
isFormShown: form === 'url' ? !this.state.isFormShown : false,
isIpfsFormShown: form === 'ipfs' ? !this.state.isIpfsFormShown : false
})
}
private async getFile(url: string) {
const file: FilePublish = {
url,
contentType: '',
found: false // non-standard
}
try {
const response = await axios({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
url: `${serviceUri}/api/v1/urlcheck`,
data: { url },
cancelToken: this.signal.token
})
const { contentLength, contentType, found } = response.data.result
if (contentLength) file.contentLength = contentLength
if (contentType) {
file.contentType = contentType
file.compression = cleanupContentType(contentType)
}
file.found = found
return file
} catch (error) {
!axios.isCancel(error) && Logger.error(error.message)
}
}
private addFile = async (url: string) => {
// check for duplicate urls
const duplicateFiles = this.props.files.filter(props =>
url.includes(props.url)
)
if (duplicateFiles.length > 0) {
return this.setState({
isFormShown: false, isFormShown: false,
isIpfsFormShown: false isIpfsFormShown: false
})
} }
// for canceling axios requests const file: FilePublish | undefined = await this.getFile(url)
public signal = axios.CancelToken.source() file && this.props.files.push(file)
public componentWillUnmount() { const event = {
this.signal.cancel() currentTarget: {
name: 'files',
value: this.props.files
}
} }
this.props.onChange(event as any)
private toggleForm = (e: Event, form: string) => { this.setState({
e.preventDefault() isFormShown: false,
isIpfsFormShown: false
})
this.setState({ this.forceUpdate()
isFormShown: form === 'url' ? !this.state.isFormShown : false, }
isIpfsFormShown:
form === 'ipfs' ? !this.state.isIpfsFormShown : false private removeFile = (index: number) => {
}) this.props.files.splice(index, 1)
const event = {
currentTarget: {
name: 'files',
value: this.props.files
}
} }
this.props.onChange(event as any)
this.forceUpdate()
}
private async getFile(url: string) { public render() {
const file: FilePublish = { const { files, help, placeholder, name, onChange } = this.props
url, const { isFormShown, isIpfsFormShown } = this.state
contentType: '',
found: false // non-standard
}
try { return (
const response = await axios({ <>
method: 'POST', {help && <Help>{help}</Help>}
headers: { 'Content-Type': 'application/json' },
url: `${serviceUri}/api/v1/urlcheck`,
data: { url },
cancelToken: this.signal.token
})
const { contentLength, contentType, found } = response.data.result {/* Use hidden input to collect files */}
<input
type="hidden"
name={name}
value={JSON.stringify(files)}
onChange={onChange}
data-testid="files"
/>
if (contentLength) file.contentLength = contentLength <div className={styles.newItems}>
if (contentType) { {files.length > 0 && (
file.contentType = contentType <ul className={styles.itemsList}>
file.compression = cleanupContentType(contentType) {files.map((item: any, index: number) => (
} <Item
key={shortid.generate()}
file.found = found item={item}
removeFile={() => this.removeFile(index)}
return file
} catch (error) {
!axios.isCancel(error) && Logger.error(error.message)
}
}
private addFile = async (url: string) => {
// check for duplicate urls
const duplicateFiles = this.props.files.filter(props =>
url.includes(props.url)
)
if (duplicateFiles.length > 0) {
return this.setState({
isFormShown: false,
isIpfsFormShown: false
})
}
const file: FilePublish | undefined = await this.getFile(url)
file && this.props.files.push(file)
const event = {
currentTarget: {
name: 'files',
value: this.props.files
}
}
this.props.onChange(event as any)
this.setState({
isFormShown: false,
isIpfsFormShown: false
})
this.forceUpdate()
}
private removeFile = (index: number) => {
this.props.files.splice(index, 1)
const event = {
currentTarget: {
name: 'files',
value: this.props.files
}
}
this.props.onChange(event as any)
this.forceUpdate()
}
public render() {
const { files, help, placeholder, name, onChange } = this.props
const { isFormShown, isIpfsFormShown } = this.state
return (
<>
{help && <Help>{help}</Help>}
{/* Use hidden input to collect files */}
<input
type="hidden"
name={name}
value={JSON.stringify(files)}
onChange={onChange}
data-testid="files"
/> />
))}
</ul>
)}
<div className={styles.newItems}> {buttons.map(button => {
{files.length > 0 && ( const isActive =
<ul className={styles.itemsList}> (button.id === 'url' && isFormShown) ||
{files.map((item: any, index: number) => ( (button.id === 'ipfs' && isIpfsFormShown)
<Item
key={shortid.generate()}
item={item}
removeFile={() => this.removeFile(index)}
/>
))}
</ul>
)}
{buttons.map(button => { return (
const isActive = <Button
(button.id === 'url' && isFormShown) || key={shortid.generate()}
(button.id === 'ipfs' && isIpfsFormShown) link
onClick={(e: Event) => this.toggleForm(e, button.id)}
>
{isActive ? button.titleActive : button.title}
</Button>
)
})}
return ( {isFormShown && (
<Button <ItemForm placeholder={placeholder} addFile={this.addFile} />
key={shortid.generate()} )}
link
onClick={(e: Event) =>
this.toggleForm(e, button.id)
}
>
{isActive ? button.titleActive : button.title}
</Button>
)
})}
{isFormShown && ( {isIpfsFormShown && (
<ItemForm <Suspense fallback={<Spinner message="Loading..." />}>
placeholder={placeholder} <Ipfs addFile={this.addFile} />
addFile={this.addFile} </Suspense>
/> )}
)} </div>
</>
{isIpfsFormShown && ( )
<Suspense fallback={<Spinner message="Loading..." />}> }
<Ipfs addFile={this.addFile} />
</Suspense>
)}
</div>
</>
)
}
} }