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 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 (
@ -36,5 +33,4 @@ export default class App extends Component {
} }
} }
App.contextType = User App.contextType = User

View File

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

View File

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

View File

@ -9,153 +9,151 @@ import ReactGA from 'react-ga'
import cleanupContentType from '../../../utils/cleanupContentType' import cleanupContentType from '../../../utils/cleanupContentType'
export const messages: any = { export const messages: any = {
99: 'Decrypting file URL...', 99: 'Decrypting file URL...',
0: '1/3<br />Asking for agreement signature...', 0: '1/3<br />Asking for agreement signature...',
1: '1/3<br />Agreement initialized.', 1: '1/3<br />Agreement initialized.',
2: '2/3<br />Asking for two payment confirmations...', 2: '2/3<br />Asking for two payment confirmations...',
3: '2/3<br />Payment confirmed. Requesting access...', 3: '2/3<br />Payment confirmed. Requesting access...',
4: '3/3<br /> Access granted. Consuming file...' 4: '3/3<br /> Access granted. Consuming file...'
} }
interface AssetFileProps { interface AssetFileProps {
file: File file: File
ddo: DDO ddo: DDO
} }
interface AssetFileState { interface AssetFileState {
isLoading: boolean isLoading: boolean
error: string error: string
step: number step: number
} }
export default class AssetFile extends PureComponent< export default class AssetFile extends PureComponent<
AssetFileProps, AssetFileProps,
AssetFileState 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, isLoading: false,
error: '', error: `${error.message}. Sorry about that, can you try again?`
step: 99 })
ReactGA.event({
category: 'Purchase',
action: 'purchaseAsset-error ' + error.message
})
} }
}
private resetState = () => public render() {
this.setState({ const { ddo, file } = this.props
isLoading: true, const { isLoading, error, step } = this.state
error: '', const { isLogged } = this.context
step: 99 const { index, contentType, contentLength } = file
})
private purchaseAsset = async (ddo: DDO, index: number) => { return (
this.resetState() <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({ {isLoading ? (
category: 'Purchase', <Spinner message={messages[step]} />
action: 'purchaseAsset-start ' + ddo.id ) : (
}) <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 {error !== '' && <div className={styles.error}>{error}</div>}
</div>
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>
)
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,23 +5,22 @@ import App from './App'
import * as serviceWorker from './serviceWorker' import * as serviceWorker from './serviceWorker'
function renderToDOM() { function renderToDOM() {
const root = document.getElementById('root') const root = document.getElementById('root')
if (root !== null) { if (root !== null) {
ReactDOM.render( ReactDOM.render(
<UserProvider> <UserProvider>
<App /> <App />
</UserProvider>, </UserProvider>,
root root
) )
} }
} }
export { renderToDOM } 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,22 +1,22 @@
import { MetaData } from '@oceanprotocol/squid' import { MetaData } from '@oceanprotocol/squid'
const AssetModel: MetaData = { const AssetModel: MetaData = {
// OEP-08 Attributes // OEP-08 Attributes
// https://github.com/oceanprotocol/OEPs/tree/master/8 // https://github.com/oceanprotocol/OEPs/tree/master/8
main: { main: {
type: 'dataset', type: 'dataset',
name: '', name: '',
dateCreated: '', dateCreated: '',
author: '', author: '',
license: '', license: '',
price: '', price: '',
files: [] files: []
}, },
additionalInformation: { additionalInformation: {
description: '', description: '',
copyrightHolder: '', copyrightHolder: '',
categories: [] categories: []
} }
} }
export default AssetModel export default AssetModel

View File

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

View File

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