1
0
mirror of https://github.com/oceanprotocol/commons.git synced 2023-03-15 18:03:00 +01:00
This commit is contained in:
Max Berman 2019-12-16 16:40:04 +01:00
parent 8023e30061
commit 125df58464
13 changed files with 712 additions and 717 deletions

View File

@ -5,12 +5,9 @@ import Footer from './components/organisms/Footer'
import Spinner from './components/atoms/Spinner'
import { User } from './context'
import Routes from './Routes'
import {commonsNetwork} from './components/molecules/NetworkSwitcher'
import './styles/global.scss'
import styles from './App.module.scss'
console.log(commonsNetwork)
export default class App extends Component {
public render() {
return (
@ -36,5 +33,4 @@ export default class App extends Component {
}
}
App.contextType = User

View File

@ -1,12 +1,15 @@
import React, { useState } from 'react'
import { CONNECTIONS } from '../../config'
/* TEMP NETWORK SWITCHER */
/* 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]
const networkFromParam = urlParams.get('network') || 'pacific'
const idx = Object.keys(CONNECTIONS).indexOf(networkFromParam)
const commonsNetwork = Object.values(CONNECTIONS)[idx] // TypeScript won't let me access CONNECTIONS[networkFromParam] directly
console.log(commonsNetwork)
export { commonsNetwork }
export function NetworkSwitcher() {
return null
}

View File

@ -8,34 +8,34 @@ import menu from '../../data/menu'
import meta from '../../data/meta.json'
const MenuItem = ({ item }: { item: any }) => (
<NavLink
to={item.link}
className={styles.link}
activeClassName={styles.linkActive}
exact
>
{item.title}
</NavLink>
<NavLink
to={item.link}
className={styles.link}
activeClassName={styles.linkActive}
exact
>
{item.title}
</NavLink>
)
export default class Header extends PureComponent {
public render() {
return (
<header className={styles.header}>
<div className={styles.headerContent}>
<NavLink to="/" className={styles.headerLogo}>
<Logo className={styles.headerLogoImage} />
<h1 className={styles.headerTitle}>{meta.title}</h1>
</NavLink>
public render() {
return (
<header className={styles.header}>
<div className={styles.headerContent}>
<NavLink to="/" className={styles.headerLogo}>
<Logo className={styles.headerLogoImage} />
<h1 className={styles.headerTitle}>{meta.title}</h1>
</NavLink>
<nav className={styles.headerMenu}>
{menu.map(item => (
<MenuItem key={item.title} item={item} />
))}
<AccountStatus className={styles.accountStatus} />
</nav>
</div>
</header>
)
}
<nav className={styles.headerMenu}>
{menu.map(item => (
<MenuItem key={item.title} item={item} />
))}
<AccountStatus className={styles.accountStatus} />
</nav>
</div>
</header>
)
}
}

View File

@ -9,153 +9,151 @@ import ReactGA from 'react-ga'
import cleanupContentType from '../../../utils/cleanupContentType'
export const messages: any = {
99: 'Decrypting file URL...',
0: '1/3<br />Asking for agreement signature...',
1: '1/3<br />Agreement initialized.',
2: '2/3<br />Asking for two payment confirmations...',
3: '2/3<br />Payment confirmed. Requesting access...',
4: '3/3<br /> Access granted. Consuming file...'
99: 'Decrypting file URL...',
0: '1/3<br />Asking for agreement signature...',
1: '1/3<br />Agreement initialized.',
2: '2/3<br />Asking for two payment confirmations...',
3: '2/3<br />Payment confirmed. Requesting access...',
4: '3/3<br /> Access granted. Consuming file...'
}
interface AssetFileProps {
file: File
ddo: DDO
file: File
ddo: DDO
}
interface AssetFileState {
isLoading: boolean
error: string
step: number
isLoading: boolean
error: string
step: number
}
export default class AssetFile extends PureComponent<
AssetFileProps,
AssetFileState
AssetFileProps,
AssetFileState
> {
public static contextType = User
public static contextType = User
public state = {
public state = {
isLoading: false,
error: '',
step: 99
}
private resetState = () =>
this.setState({
isLoading: true,
error: '',
step: 99
})
private purchaseAsset = async (ddo: DDO, index: number) => {
this.resetState()
ReactGA.event({
category: 'Purchase',
action: 'purchaseAsset-start ' + ddo.id
})
const { ocean } = this.context
try {
const accounts = await ocean.accounts.list()
const service = ddo.findServiceByType('access')
const agreements = await ocean.keeper.conditions.accessSecretStoreCondition.getGrantedDidByConsumer(
accounts[0].id
)
const agreement = agreements.find((element: any) => {
return element.did === ddo.id
})
let agreementId
if (agreement) {
;({ agreementId } = agreement)
} else {
agreementId = await ocean.assets
.order(ddo.id, service.index, accounts[0])
.next((step: number) => this.setState({ step }))
}
// manually add another step here for better UX
this.setState({ step: 4 })
const path = await ocean.assets.consume(
agreementId,
ddo.id,
service.index,
accounts[0],
'',
index
)
Logger.log('path', path)
ReactGA.event({
category: 'Purchase',
action: 'purchaseAsset-end ' + ddo.id
})
this.setState({ isLoading: false })
} catch (error) {
Logger.error('error', error.message)
this.setState({
isLoading: false,
error: '',
step: 99
error: `${error.message}. Sorry about that, can you try again?`
})
ReactGA.event({
category: 'Purchase',
action: 'purchaseAsset-error ' + error.message
})
}
}
private resetState = () =>
this.setState({
isLoading: true,
error: '',
step: 99
})
public render() {
const { ddo, file } = this.props
const { isLoading, error, step } = this.state
const { isLogged } = this.context
const { index, contentType, contentLength } = file
private purchaseAsset = async (ddo: DDO, index: number) => {
this.resetState()
return (
<div className={styles.fileWrap}>
<ul key={index} className={styles.file}>
{contentType || contentLength ? (
<>
<li>{cleanupContentType(contentType)}</li>
<li>
{contentLength && contentLength !== '0'
? filesize(contentLength)
: ''}
</li>
{/* <li>{encoding}</li> */}
{/* <li>{compression}</li> */}
</>
) : (
<li className={styles.empty}>No file info available</li>
)}
</ul>
ReactGA.event({
category: 'Purchase',
action: 'purchaseAsset-start ' + ddo.id
})
{isLoading ? (
<Spinner message={messages[step]} />
) : (
<Market.Consumer>
{market => (
<Button
primary
className={styles.buttonMain}
// weird 0 hack so TypeScript is happy
onClick={() => this.purchaseAsset(ddo, index || 0)}
disabled={!isLogged || !market.networkMatch}
name="Download"
>
Get file
</Button>
)}
</Market.Consumer>
)}
const { ocean } = this.context
try {
const accounts = await ocean.accounts.list()
const service = ddo.findServiceByType('access')
const agreements = await ocean.keeper.conditions.accessSecretStoreCondition.getGrantedDidByConsumer(
accounts[0].id
)
const agreement = agreements.find((element: any) => {
return element.did === ddo.id
})
let agreementId
if (agreement) {
;({ agreementId } = agreement)
} else {
agreementId = await ocean.assets
.order(ddo.id, service.index, accounts[0])
.next((step: number) => this.setState({ step }))
}
// manually add another step here for better UX
this.setState({ step: 4 })
const path = await ocean.assets.consume(
agreementId,
ddo.id,
service.index,
accounts[0],
'',
index
)
Logger.log('path', path)
ReactGA.event({
category: 'Purchase',
action: 'purchaseAsset-end ' + ddo.id
})
this.setState({ isLoading: false })
} catch (error) {
Logger.error('error', error.message)
this.setState({
isLoading: false,
error: `${error.message}. Sorry about that, can you try again?`
})
ReactGA.event({
category: 'Purchase',
action: 'purchaseAsset-error ' + error.message
})
}
}
public render() {
const { ddo, file } = this.props
const { isLoading, error, step } = this.state
const { isLogged } = this.context
const { index, contentType, contentLength } = file
return (
<div className={styles.fileWrap}>
<ul key={index} className={styles.file}>
{contentType || contentLength ? (
<>
<li>{cleanupContentType(contentType)}</li>
<li>
{contentLength && contentLength !== '0'
? filesize(contentLength)
: ''}
</li>
{/* <li>{encoding}</li> */}
{/* <li>{compression}</li> */}
</>
) : (
<li className={styles.empty}>No file info available</li>
)}
</ul>
{isLoading ? (
<Spinner message={messages[step]} />
) : (
<Market.Consumer>
{market => (
<Button
primary
className={styles.buttonMain}
// weird 0 hack so TypeScript is happy
onClick={() =>
this.purchaseAsset(ddo, index || 0)
}
disabled={!isLogged || !market.networkMatch}
name="Download"
>
Get file
</Button>
)}
</Market.Consumer>
)}
{error !== '' && <div className={styles.error}>{error}</div>}
</div>
)
}
{error !== '' && <div className={styles.error}>{error}</div>}
</div>
)
}
}

View File

@ -7,47 +7,47 @@ import { requestFromFaucet } from '../ocean'
const bip39 = require('bip39')
export class BurnerWalletProvider {
private web3: Web3
private web3: Web3
public constructor() {
// Default
this.web3 = null as any
public constructor() {
// Default
this.web3 = null as any
}
public async isLogged() {
if (localStorage.getItem('seedphrase') !== null) {
return true
}
return false
}
public async startLogin() {
let mnemonic
const isLogged = await this.isLogged()
if (isLogged) {
mnemonic = localStorage.getItem('seedphrase')
} else {
mnemonic = bip39.generateMnemonic()
localStorage.setItem('seedphrase', mnemonic)
}
public async isLogged() {
if (localStorage.getItem('seedphrase') !== null) {
return true
}
return false
}
localStorage.setItem('logType', 'BurnerWallet')
const provider = new HDWalletProvider(mnemonic, nodeUri, 0, 1)
this.web3 = new Web3(provider as any)
const accounts = await this.web3.eth.getAccounts()
const balance = await this.web3.eth.getBalance(accounts[0])
public async startLogin() {
let mnemonic
const isLogged = await this.isLogged()
// fill with Ether if account balance is empty
balance === '0' && (await requestFromFaucet(provider.getAddress(0)))
}
if (isLogged) {
mnemonic = localStorage.getItem('seedphrase')
} else {
mnemonic = bip39.generateMnemonic()
localStorage.setItem('seedphrase', mnemonic)
}
public async logout() {
// localStorage.removeItem('seedphrase')
localStorage.removeItem('logType')
}
localStorage.setItem('logType', 'BurnerWallet')
const provider = new HDWalletProvider(mnemonic, nodeUri, 0, 1)
this.web3 = new Web3(provider as any)
const accounts = await this.web3.eth.getAccounts()
const balance = await this.web3.eth.getBalance(accounts[0])
// fill with Ether if account balance is empty
balance === '0' && (await requestFromFaucet(provider.getAddress(0)))
}
public async logout() {
// localStorage.removeItem('seedphrase')
localStorage.removeItem('logType')
}
public getProvider() {
return this.web3
}
public getProvider() {
return this.web3
}
}

View File

@ -4,94 +4,94 @@ import { Market, User } from '.'
import formPublish from '../data/form-publish.json'
const categories =
(formPublish.steps[1].fields &&
formPublish.steps[1].fields.categories &&
formPublish.steps[1].fields.categories.options) ||
[]
(formPublish.steps[1].fields &&
formPublish.steps[1].fields.categories &&
formPublish.steps[1].fields.categories.options) ||
[]
interface MarketProviderProps {
ocean: Ocean
ocean: Ocean
}
interface MarketProviderState {
totalAssets: number
categories: string[]
network: string
networkMatch: boolean
totalAssets: number
categories: string[]
network: string
networkMatch: boolean
}
export default class MarketProvider extends PureComponent<
MarketProviderProps,
MarketProviderState
MarketProviderProps,
MarketProviderState
> {
public static contextType = User
public static contextType = User
public state = {
totalAssets: 0,
categories,
network: 'Pacific',
networkMatch: false
public state = {
totalAssets: 0,
categories,
network: 'Pacific',
networkMatch: false
}
public async componentDidMount() {
await this.checkCorrectUserNetwork()
}
public async componentDidUpdate(prevProps: any) {
// Using ocean prop instead of getting it from context to be able to compare.
// Cause there is no `prevContext`.
if (prevProps.ocean !== this.props.ocean) {
await this.getTotalAssets()
await this.getMarketNetwork()
await this.checkCorrectUserNetwork()
}
}
private getTotalAssets = async () => {
const searchQuery = {
offset: 1,
page: 1,
query: {},
sort: {
value: 1
}
}
public async componentDidMount() {
await this.checkCorrectUserNetwork()
try {
const { ocean } = this.props
const search = await ocean.assets.query(searchQuery)
this.setState({ totalAssets: search.totalResults })
} catch (error) {
Logger.error('Error', error.message)
}
}
public async componentDidUpdate(prevProps: any) {
// Using ocean prop instead of getting it from context to be able to compare.
// Cause there is no `prevContext`.
if (prevProps.ocean !== this.props.ocean) {
await this.getTotalAssets()
await this.getMarketNetwork()
await this.checkCorrectUserNetwork()
}
private getMarketNetwork = async () => {
try {
const { ocean } = this.props
// Set desired network to whatever Brizo is running in
const brizo = await ocean.brizo.getVersionInfo()
const network =
brizo.network.charAt(0).toUpperCase() + brizo.network.slice(1)
this.setState({ network })
} catch (error) {
Logger.error('Error', error.message)
}
}
private getTotalAssets = async () => {
const searchQuery = {
offset: 1,
page: 1,
query: {},
sort: {
value: 1
}
}
try {
const { ocean } = this.props
const search = await ocean.assets.query(searchQuery)
this.setState({ totalAssets: search.totalResults })
} catch (error) {
Logger.error('Error', error.message)
}
private async checkCorrectUserNetwork() {
if (this.context.network === this.state.network) {
this.setState({ networkMatch: true })
} else {
this.setState({ networkMatch: false })
}
}
private getMarketNetwork = async () => {
try {
const { ocean } = this.props
// Set desired network to whatever Brizo is running in
const brizo = await ocean.brizo.getVersionInfo()
const network =
brizo.network.charAt(0).toUpperCase() + brizo.network.slice(1)
this.setState({ network })
} catch (error) {
Logger.error('Error', error.message)
}
}
private async checkCorrectUserNetwork() {
if (this.context.network === this.state.network) {
this.setState({ networkMatch: true })
} else {
this.setState({ networkMatch: false })
}
}
public render() {
return (
<Market.Provider value={this.state}>
{this.props.children}
</Market.Provider>
)
}
public render() {
return (
<Market.Provider value={this.state}>
{this.props.children}
</Market.Provider>
)
}
}

View File

@ -1,48 +1,48 @@
import Web3 from 'web3'
export class MetamaskProvider {
private web3: Web3
private web3: Web3
public constructor() {
// Default
this.web3 = null as any
// Modern dapp browsers
if (window.ethereum) {
this.web3 = new Web3(window.ethereum)
}
// Legacy dapp browsers
else if (window.web3) {
this.web3 = new Web3(window.web3.currentProvider)
}
public constructor() {
// Default
this.web3 = null as any
// Modern dapp browsers
if (window.ethereum) {
this.web3 = new Web3(window.ethereum)
}
// Legacy dapp browsers
else if (window.web3) {
this.web3 = new Web3(window.web3.currentProvider)
}
}
public async isAvailable() {
return this.web3 !== null
}
public async isAvailable() {
return this.web3 !== null
}
public async isLogged() {
if (this.web3 === null) return false
if ((await this.web3.eth.getAccounts()).length > 0) {
return true
}
return false
public async isLogged() {
if (this.web3 === null) return false
if ((await this.web3.eth.getAccounts()).length > 0) {
return true
}
return false
}
public async startLogin() {
try {
await window.ethereum.enable()
localStorage.setItem('logType', 'Metamask')
} catch (error) {
return false
}
public async startLogin() {
try {
await window.ethereum.enable()
localStorage.setItem('logType', 'Metamask')
} catch (error) {
return false
}
}
public async logout() {
localStorage.removeItem('logType')
// reload page?
}
public async logout() {
localStorage.removeItem('logType')
// reload page?
}
public getProvider() {
return this.web3
}
public getProvider() {
return this.web3
}
}

View File

@ -8,227 +8,227 @@ import MarketProvider from './MarketProvider'
import { MetamaskProvider } from './MetamaskProvider'
import { BurnerWalletProvider } from './BurnerWalletProvider'
import { NetworkSwitcher } from '../components/molecules/NetworkSwitcher'
const POLL_ACCOUNTS = 1000 // every 1s
const POLL_NETWORK = POLL_ACCOUNTS * 60 // every 1 min
const DEFAULT_WEB3 = new Web3(new Web3.providers.HttpProvider(nodeUri)) // default web3
interface UserProviderState {
isLogged: boolean
isBurner: boolean
isWeb3Capable: boolean
isLoading: boolean
account: string
balance: {
eth: number
ocn: number
}
network: string
web3: Web3
ocean: Ocean
requestFromFaucet(account: string): Promise<FaucetResponse>
loginMetamask(): Promise<any>
loginBurnerWallet(): Promise<any>
logoutBurnerWallet(): Promise<any>
message: string
isLogged: boolean
isBurner: boolean
isWeb3Capable: boolean
isLoading: boolean
account: string
balance: {
eth: number
ocn: number
}
network: string
web3: Web3
ocean: Ocean
requestFromFaucet(account: string): Promise<FaucetResponse>
loginMetamask(): Promise<any>
loginBurnerWallet(): Promise<any>
logoutBurnerWallet(): Promise<any>
message: string
}
export default class UserProvider extends PureComponent<{}, UserProviderState> {
private loginMetamask = async () => {
const metamaskProvider = new MetamaskProvider()
await metamaskProvider.startLogin()
const web3 = metamaskProvider.getProvider()
this.setState(
{
isLogged: true,
isBurner: false,
web3
},
() => {
this.loadOcean()
}
)
}
private loginMetamask = async () => {
const metamaskProvider = new MetamaskProvider()
await metamaskProvider.startLogin()
const web3 = metamaskProvider.getProvider()
this.setState(
{
isLogged: true,
isBurner: false,
web3
},
() => {
this.loadOcean()
}
)
}
private loginBurnerWallet = async () => {
const burnerwalletProvider = new BurnerWalletProvider()
await burnerwalletProvider.startLogin()
const web3 = burnerwalletProvider.getProvider()
this.setState(
{
isLogged: true,
isBurner: true,
web3
},
() => {
this.loadOcean()
}
)
}
private loginBurnerWallet = async () => {
const burnerwalletProvider = new BurnerWalletProvider()
await burnerwalletProvider.startLogin()
const web3 = burnerwalletProvider.getProvider()
this.setState(
{
isLogged: true,
isBurner: true,
web3
},
() => {
this.loadOcean()
}
)
}
private logoutBurnerWallet = async () => {
const burnerwalletProvider = new BurnerWalletProvider()
await burnerwalletProvider.logout()
}
private logoutBurnerWallet = async () => {
const burnerwalletProvider = new BurnerWalletProvider()
await burnerwalletProvider.logout()
}
public state = {
public state = {
isLogged: false,
isBurner: false,
isWeb3Capable: Boolean(window.web3 || window.ethereum),
isLoading: true,
balance: {
eth: 0,
ocn: 0
},
network: '',
web3: DEFAULT_WEB3,
account: '',
ocean: {} as any,
requestFromFaucet: () => requestFromFaucet(''),
loginMetamask: () => this.loginMetamask(),
loginBurnerWallet: () => this.loginBurnerWallet(),
logoutBurnerWallet: () => this.logoutBurnerWallet(),
message: 'Connecting to Ocean...'
}
private accountsInterval: any = null
private networkInterval: any = null
public async componentDidMount() {
await this.bootstrap()
}
private initAccountsPoll() {
if (!this.accountsInterval) {
this.accountsInterval = setInterval(this.fetchAccounts, POLL_ACCOUNTS)
}
}
private initNetworkPoll() {
if (!this.networkInterval) {
this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK)
}
}
private loadDefaultWeb3 = async () => {
this.setState(
{
isLogged: false,
isBurner: false,
isWeb3Capable: Boolean(window.web3 || window.ethereum),
isLoading: true,
balance: {
eth: 0,
ocn: 0
},
network: '',
web3: DEFAULT_WEB3,
account: '',
ocean: {} as any,
requestFromFaucet: () => requestFromFaucet(''),
loginMetamask: () => this.loginMetamask(),
loginBurnerWallet: () => this.loginBurnerWallet(),
logoutBurnerWallet: () => this.logoutBurnerWallet(),
message: 'Connecting to Ocean...'
}
web3: DEFAULT_WEB3
},
() => {
this.loadOcean()
}
)
}
private accountsInterval: any = null
private loadOcean = async () => {
const { ocean } = await provideOcean(this.state.web3)
this.setState({ ocean, isLoading: false }, () => {
this.initNetworkPoll()
this.initAccountsPoll()
this.fetchNetwork()
this.fetchAccounts()
})
}
private networkInterval: any = null
private bootstrap = async () => {
const logType = localStorage.getItem('logType')
const metamaskProvider = new MetamaskProvider()
public async componentDidMount() {
await this.bootstrap()
}
private initAccountsPoll() {
if (!this.accountsInterval) {
this.accountsInterval = setInterval(
this.fetchAccounts,
POLL_ACCOUNTS
)
}
}
private initNetworkPoll() {
if (!this.networkInterval) {
this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK)
}
}
private loadDefaultWeb3 = async () => {
this.setState(
switch (logType) {
case 'Metamask':
if (
(await metamaskProvider.isAvailable()) &&
(await metamaskProvider.isLogged())
) {
const web3 = metamaskProvider.getProvider()
this.setState(
{
isLogged: false,
isBurner: false,
web3: DEFAULT_WEB3
isLogged: true,
web3
},
() => {
this.loadOcean()
this.loadOcean()
}
)
}
private loadOcean = async () => {
const { ocean } = await provideOcean(this.state.web3)
this.setState({ ocean, isLoading: false }, () => {
this.initNetworkPoll()
this.initAccountsPoll()
this.fetchNetwork()
this.fetchAccounts()
})
}
private bootstrap = async () => {
const logType = localStorage.getItem('logType')
const metamaskProvider = new MetamaskProvider()
switch (logType) {
case 'Metamask':
if (
(await metamaskProvider.isAvailable()) &&
(await metamaskProvider.isLogged())
) {
const web3 = metamaskProvider.getProvider()
this.setState(
{
isLogged: true,
web3
},
() => {
this.loadOcean()
}
)
} else {
this.loadDefaultWeb3()
}
break
case 'BurnerWallet':
this.loginBurnerWallet()
break
default:
this.loginBurnerWallet()
break
)
} else {
this.loadDefaultWeb3()
}
break
case 'BurnerWallet':
this.loginBurnerWallet()
break
default:
this.loginBurnerWallet()
break
}
}
private fetchAccounts = async () => {
const { ocean, isLogged } = this.state
private fetchAccounts = async () => {
const { ocean, isLogged } = this.state
if (isLogged) {
let accounts
if (isLogged) {
let accounts
// Modern dapp browsers
if (window.ethereum && !isLogged) {
// simply set to empty, and have user click a button somewhere
// to initiate account unlocking
accounts = []
// Modern dapp browsers
if (window.ethereum && !isLogged) {
// simply set to empty, and have user click a button somewhere
// to initiate account unlocking
accounts = []
// alternatively, automatically prompt for account unlocking
// await this.unlockAccounts()
}
// alternatively, automatically prompt for account unlocking
// await this.unlockAccounts()
}
accounts = await ocean.accounts.list()
accounts = await ocean.accounts.list()
if (accounts.length > 0) {
const account = await accounts[0].getId()
if (accounts.length > 0) {
const account = await accounts[0].getId()
if (account !== this.state.account) {
this.setState({
account,
isLogged: true,
requestFromFaucet: () => requestFromFaucet(account)
})
if (account !== this.state.account) {
this.setState({
account,
isLogged: true,
requestFromFaucet: () => requestFromFaucet(account)
})
await this.fetchBalance(accounts[0])
}
} else {
!isLogged && this.setState({ isLogged: false, account: '' })
}
await this.fetchBalance(accounts[0])
}
} else {
!isLogged && this.setState({ isLogged: false, account: '' })
}
}
}
private fetchBalance = async (account: Account) => {
const balance = await account.getBalance()
const { eth, ocn } = balance
if (eth !== this.state.balance.eth || ocn !== this.state.balance.ocn) {
this.setState({ balance: { eth, ocn } })
}
private fetchBalance = async (account: Account) => {
const balance = await account.getBalance()
const { eth, ocn } = balance
if (eth !== this.state.balance.eth || ocn !== this.state.balance.ocn) {
this.setState({ balance: { eth, ocn } })
}
}
private fetchNetwork = async () => {
const { ocean } = this.state
let network = 'Unknown'
if (ocean.keeper) {
network = await ocean.keeper.getNetworkName()
}
network !== this.state.network && this.setState({ network })
private fetchNetwork = async () => {
const { ocean } = this.state
let network = 'Unknown'
if (ocean.keeper) {
network = await ocean.keeper.getNetworkName()
}
network !== this.state.network && this.setState({ network })
}
public render() {
return (
<User.Provider value={this.state}>
<MarketProvider ocean={this.state.ocean}>
{this.props.children}
</MarketProvider>
</User.Provider>
)
}
public render() {
return (
<User.Provider value={this.state}>
<MarketProvider ocean={this.state.ocean}>
<NetworkSwitcher />
{this.props.children}
</MarketProvider>
</User.Provider>
)
}
}

View File

@ -1,33 +1,33 @@
import React from 'react'
export const User = React.createContext({
isLogged: false,
isBurner: false,
isWeb3Capable: false,
isLoading: false,
account: '',
web3: {},
ocean: {},
balance: {
eth: 0,
ocn: 0
},
network: '',
requestFromFaucet: () => {
/* empty */
},
loginMetamask: () => {
/* empty */
},
loginBurnerWallet: () => {
/* empty */
},
message: ''
isLogged: false,
isBurner: false,
isWeb3Capable: false,
isLoading: false,
account: '',
web3: {},
ocean: {},
balance: {
eth: 0,
ocn: 0
},
network: '',
requestFromFaucet: () => {
/* empty */
},
loginMetamask: () => {
/* empty */
},
loginBurnerWallet: () => {
/* empty */
},
message: ''
})
export const Market = React.createContext({
totalAssets: 0,
categories: [''],
network: '',
networkMatch: false
totalAssets: 0,
categories: [''],
network: '',
networkMatch: false
})

View File

@ -5,23 +5,22 @@ import App from './App'
import * as serviceWorker from './serviceWorker'
function renderToDOM() {
const root = document.getElementById('root')
const root = document.getElementById('root')
if (root !== null) {
ReactDOM.render(
<UserProvider>
<App />
</UserProvider>,
root
)
}
if (root !== null) {
ReactDOM.render(
<UserProvider>
<App />
</UserProvider>,
root
)
}
}
export { renderToDOM }
renderToDOM()
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA

View File

@ -1,22 +1,22 @@
import { MetaData } from '@oceanprotocol/squid'
const AssetModel: MetaData = {
// OEP-08 Attributes
// https://github.com/oceanprotocol/OEPs/tree/master/8
main: {
type: 'dataset',
name: '',
dateCreated: '',
author: '',
license: '',
price: '',
files: []
},
additionalInformation: {
description: '',
copyrightHolder: '',
categories: []
}
// OEP-08 Attributes
// https://github.com/oceanprotocol/OEPs/tree/master/8
main: {
type: 'dataset',
name: '',
dateCreated: '',
author: '',
license: '',
price: '',
files: []
},
additionalInformation: {
description: '',
copyrightHolder: '',
categories: []
}
}
export default AssetModel

View File

@ -10,48 +10,48 @@ const history = createMemoryHistory()
const location = createLocation('/faucet')
const setup = () => {
const utils = render(
<User.Provider value={userMockConnected}>
<Market.Provider
value={{
network: 'pacific',
totalAssets: 100,
categories: [''],
networkMatch: true
}}
>
<MemoryRouter>
<Faucet
history={history}
location={location}
match={{ params: '', path: '', url: '', isExact: true }}
/>
</MemoryRouter>
</Market.Provider>
</User.Provider>
)
const button = utils.getByText('Request ETH')
const { container } = utils
return { button, container, ...utils }
const utils = render(
<User.Provider value={userMockConnected}>
<Market.Provider
value={{
network: 'pacific',
totalAssets: 100,
categories: [''],
networkMatch: true
}}
>
<MemoryRouter>
<Faucet
history={history}
location={location}
match={{ params: '', path: '', url: '', isExact: true }}
/>
</MemoryRouter>
</Market.Provider>
</User.Provider>
)
const button = utils.getByText('Request ETH')
const { container } = utils
return { button, container, ...utils }
}
describe('Faucet', () => {
it('renders without crashing', () => {
const { container } = setup()
expect(container.firstChild).toBeInTheDocument()
})
it('renders without crashing', () => {
const { container } = setup()
expect(container.firstChild).toBeInTheDocument()
})
it('shows actions when connected', () => {
const { button } = setup()
expect(button).toBeInTheDocument()
expect(button).not.toHaveAttribute('disabled')
})
it('shows actions when connected', () => {
const { button } = setup()
expect(button).toBeInTheDocument()
expect(button).not.toHaveAttribute('disabled')
})
it('fires requestFromFaucet', async () => {
await act(async () => {
const { button } = setup()
fireEvent.click(button)
})
expect(userMockConnected.requestFromFaucet).toHaveBeenCalledTimes(1)
it('fires requestFromFaucet', async () => {
await act(async () => {
const { button } = setup()
fireEvent.click(button)
})
expect(userMockConnected.requestFromFaucet).toHaveBeenCalledTimes(1)
})
})

View File

@ -13,153 +13,152 @@ import Content from '../components/atoms/Content'
import withTracker from '../hoc/withTracker'
interface SearchProps {
location: Location
history: History
location: Location
history: History
}
interface SearchState {
results: any[]
totalResults: number
offset: number
totalPages: number
currentPage: number
isLoading: boolean
searchTerm: string
searchCategories: string
results: any[]
totalResults: number
offset: number
totalPages: number
currentPage: number
isLoading: boolean
searchTerm: string
searchCategories: string
}
class Search extends PureComponent<SearchProps, SearchState> {
public static contextType = User
public static contextType = User
public state = {
results: [],
totalResults: 0,
offset: 25,
totalPages: 1,
currentPage: 1,
isLoading: true,
searchTerm: '',
searchCategories: ''
public state = {
results: [],
totalResults: 0,
offset: 25,
totalPages: 1,
currentPage: 1,
isLoading: true,
searchTerm: '',
searchCategories: ''
}
public async componentDidMount() {
const { search } = this.props.location
const { text, page, categories } = queryString.parse(search)
if (text) {
await this.setState({
searchTerm: decodeURIComponent(`${text}`)
})
}
public async componentDidMount() {
const { search } = this.props.location
const { text, page, categories } = queryString.parse(search)
if (text) {
await this.setState({
searchTerm: decodeURIComponent(`${text}`)
})
}
if (categories) {
await this.setState({
searchCategories: decodeURIComponent(`${categories}`)
})
}
// switch to respective page if query string is present
if (page) {
const currentPage = Number(page)
await this.setState({ currentPage })
}
this.searchAssets()
if (categories) {
await this.setState({
searchCategories: decodeURIComponent(`${categories}`)
})
}
private searchAssets = async () => {
const { ocean } = this.context
const { offset, currentPage, searchTerm, searchCategories } = this.state
const queryValues =
searchCategories !== '' && searchTerm !== ''
? { text: [searchTerm], categories: [searchCategories] }
: searchCategories !== '' && searchTerm === ''
? { categories: [searchCategories] }
: { text: [searchTerm] }
const searchQuery = {
offset,
page: currentPage,
query: {
...queryValues
},
sort: {
created: -1
}
}
try {
const search = await ocean.assets.query(searchQuery)
this.setState({
results: search.results,
totalResults: search.totalResults,
totalPages: search.totalPages,
isLoading: false
})
} catch (error) {
Logger.error(error.message)
this.setState({ isLoading: false })
}
// switch to respective page if query string is present
if (page) {
const currentPage = Number(page)
await this.setState({ currentPage })
}
private handlePageClick = async (data: { selected: number }) => {
// react-pagination starts counting at 0, we start at 1
const toPage = data.selected + 1
this.searchAssets()
}
this.props.history.push({
pathname: this.props.location.pathname,
search: `?text=${this.state.searchTerm}&page=${toPage}`
})
private searchAssets = async () => {
const { ocean } = this.context
const { offset, currentPage, searchTerm, searchCategories } = this.state
await this.setState({ currentPage: toPage, isLoading: true })
await this.searchAssets()
const queryValues =
searchCategories !== '' && searchTerm !== ''
? { text: [searchTerm], categories: [searchCategories] }
: searchCategories !== '' && searchTerm === ''
? { categories: [searchCategories] }
: { text: [searchTerm] }
const searchQuery = {
offset,
page: currentPage,
query: {
...queryValues
},
sort: {
created: -1
}
}
public renderResults = () =>
this.state.isLoading ? (
<Spinner message="Searching..." />
) : this.state.results && this.state.results.length ? (
<div className={styles.results}>
{this.state.results.map((asset: any) => (
<AssetTeaser key={asset.id} asset={asset} />
))}
</div>
) : (
<div className={styles.empty}>
<p>No Data Sets Found.</p>
<Link to="/publish">+ Publish A Data Set</Link>
</div>
)
public render() {
const { totalResults, totalPages, currentPage } = this.state
return (
<Route title="Search" wide>
<Content wide>
{!this.state.isLoading && (
<h2 className={styles.resultsTitle}>
{totalResults} results for{' '}
<span>
{decodeURIComponent(
this.state.searchTerm ||
this.state.searchCategories
)}
</span>
</h2>
)}
{this.renderResults()}
<Pagination
totalPages={totalPages}
currentPage={currentPage}
handlePageClick={this.handlePageClick}
/>
</Content>
</Route>
)
try {
const search = await ocean.assets.query(searchQuery)
this.setState({
results: search.results,
totalResults: search.totalResults,
totalPages: search.totalPages,
isLoading: false
})
} catch (error) {
Logger.error(error.message)
this.setState({ isLoading: false })
}
}
private handlePageClick = async (data: { selected: number }) => {
// react-pagination starts counting at 0, we start at 1
const toPage = data.selected + 1
this.props.history.push({
pathname: this.props.location.pathname,
search: `?text=${this.state.searchTerm}&page=${toPage}`
})
await this.setState({ currentPage: toPage, isLoading: true })
await this.searchAssets()
}
public renderResults = () =>
this.state.isLoading ? (
<Spinner message="Searching..." />
) : this.state.results && this.state.results.length ? (
<div className={styles.results}>
{this.state.results.map((asset: any) => (
<AssetTeaser key={asset.id} asset={asset} />
))}
</div>
) : (
<div className={styles.empty}>
<p>No Data Sets Found.</p>
<Link to="/publish">+ Publish A Data Set</Link>
</div>
)
public render() {
const { totalResults, totalPages, currentPage } = this.state
return (
<Route title="Search" wide>
<Content wide>
{!this.state.isLoading && (
<h2 className={styles.resultsTitle}>
{totalResults} results for{' '}
<span>
{decodeURIComponent(
this.state.searchTerm || this.state.searchCategories
)}
</span>
</h2>
)}
{this.renderResults()}
<Pagination
totalPages={totalPages}
currentPage={currentPage}
handlePageClick={this.handlePageClick}
/>
</Content>
</Route>
)
}
}
export default withTracker(Search)