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

cleanup and more fine-grained bootstrap process

* new detection flow, one after the other: web3 -> network -> ocean -> accounts
* make account enabling part of bootstrap
* kick out dedicated startLogin action
* plan for modern, legacy, and non-dapp browsers
* consolidate fetchNetwork & fetchAccounts
This commit is contained in:
Matthias Kretschmann 2019-04-13 13:43:04 +02:00
parent fe695009ff
commit 3a8d6ea284
Signed by: m
GPG Key ID: 606EEEF3C479A91F
9 changed files with 159 additions and 206 deletions

View File

@ -43,45 +43,31 @@ interface AppState {
network: string network: string
web3: Web3 web3: Web3
ocean: any ocean: any
startLogin: () => void requestFromFaucet(): void
message: string message: string
} }
class App extends Component<{}, AppState> { class App extends Component<{}, AppState> {
private accountsInterval: any private accountsInterval: any = null
private networkInterval: any private networkInterval: any = null
public startLogin = (event?: any) => {
if (event) {
event.preventDefault()
}
this.startLoginProcess()
}
private requestFromFaucet = async () => { private requestFromFaucet = async () => {
if (this.state.account !== '') { try {
try { const url = `${faucetScheme}://${faucetHost}:${faucetPort}/faucet`
const response = await fetch( const response = await fetch(url, {
`${faucetScheme}://${faucetHost}:${faucetPort}/faucet`, method: 'POST',
{ headers: {
method: 'POST', Accept: 'application/json',
headers: { 'Content-Type': 'application/json'
Accept: 'application/json', },
'Content-Type': 'application/json' body: JSON.stringify({
}, address: this.state.account,
body: JSON.stringify({ agent: 'commons'
address: this.state.account, })
agent: 'commons' })
}) return response.json()
} } catch (error) {
) Logger.log('requestFromFaucet', error)
return response.json()
} catch (error) {
Logger.log('requestFromFaucet', error)
}
} else {
// no account found
} }
} }
@ -102,7 +88,6 @@ class App extends Component<{}, AppState> {
), ),
account: '', account: '',
ocean: {} as any, ocean: {} as any,
startLogin: this.startLogin,
requestFromFaucet: this.requestFromFaucet, requestFromFaucet: this.requestFromFaucet,
message: 'Connecting to Ocean...' message: 'Connecting to Ocean...'
} }
@ -116,68 +101,72 @@ class App extends Component<{}, AppState> {
private bootstrap = async () => { private bootstrap = async () => {
try { try {
if (window.web3) { //
let web3provider = new Web3(window.web3.currentProvider) // Start with Web3 detection
this.setState({ //
isWeb3: true, this.setState({ message: 'Setting up Web3...' })
message: 'Setting up Web3...'
})
// Modern dapp browsers
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
this.setState({ isWeb3: true })
}
// Legacy dapp browsers
else if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider)
this.setState({ isWeb3: true })
}
// Non-dapp browsers
else {
this.setState({ isWeb3: false })
}
// Modern & legacy dapp browsers
if (this.state.isWeb3) {
// //
// Detecting network with window.web3 // Detecting network with window.web3
// //
let isNile let isNile
await web3provider.eth.net.getId((err, netId) => { await window.web3.eth.net.getId((err, netId) => {
if (err) return if (err) return
isNile = netId === 8995 isNile = netId === 8995
const network = isNile ? 'Nile' : netId.toString() const network = isNile ? 'Nile' : netId.toString()
this.setState({ isNile, network })
if (
isNile !== this.state.isNile ||
network !== this.state.network
) {
this.setState({ isNile, network })
}
}) })
if (!isNile) { if (!isNile) {
web3provider = this.state.web3 window.web3 = this.state.web3
} }
// //
// Provide the Ocean // Provide the Ocean
// //
this.setState({ message: 'Connecting to Ocean...' }) this.setState({ message: 'Connecting to Ocean...' })
const { ocean } = await provideOcean(web3provider)
const { ocean } = await provideOcean(window.web3)
this.setState({ ocean, isLoading: false }) this.setState({ ocean, isLoading: false })
// Set proper network names now that we have Ocean // Set proper network names now that we have Ocean
const network = await ocean.keeper.getNetworkName() this.fetchNetwork()
isNile = network === 'Nile'
this.setState({ isNile, network })
// Get accounts with Ocean // Get accounts
const accounts = await ocean.accounts.list() this.fetchAccounts()
}
if (accounts.length > 0) { // Non-dapp browsers
this.setState({ else {
isLogged: true, this.setState({ message: 'Connecting to Ocean...' })
account: accounts[0].getId()
})
const balance = await accounts[0].getBalance()
this.setState({ balance })
}
} else {
//
// No Web3 browser
//
const { ocean } = await provideOcean(this.state.web3) const { ocean } = await provideOcean(this.state.web3)
this.setState({ isLoading: false }) this.setState({ ocean, isLoading: false })
const network = await ocean.keeper.getNetworkName() this.fetchNetwork()
const isNile = network === 'Nile'
this.setState({
isNile,
ocean,
network
})
} }
} catch (e) { } catch (e) {
// error in bootstrap process // error in bootstrap process
@ -188,7 +177,7 @@ class App extends Component<{}, AppState> {
} }
private initAccountsPoll() { private initAccountsPoll() {
if (!this.accountsInterval && this.state.ocean.length) { if (!this.accountsInterval) {
this.accountsInterval = setInterval( this.accountsInterval = setInterval(
this.fetchAccounts, this.fetchAccounts,
POLL_ACCOUNTS POLL_ACCOUNTS
@ -197,16 +186,28 @@ class App extends Component<{}, AppState> {
} }
private initNetworkPoll() { private initNetworkPoll() {
if (!this.networkInterval && this.state.ocean.length) { if (!this.networkInterval) {
this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK) this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK)
} }
} }
private fetchAccounts = async () => { private fetchAccounts = async () => {
const { web3 } = window const { ocean, isWeb3, isLogged, isNile } = this.state
const { ocean } = this.state
if (isWeb3) {
// Modern dapp browsers
if (window.ethereum) {
if (!isLogged && isNile) {
try {
await window.ethereum.enable()
} catch (error) {
// User denied account access...
this.accountsInterval = null
return
}
}
}
if (web3) {
const accounts = await ocean.accounts.list() const accounts = await ocean.accounts.list()
if (accounts.length > 0) { if (accounts.length > 0) {
@ -225,23 +226,16 @@ class App extends Component<{}, AppState> {
this.setState({ balance }) this.setState({ balance })
} }
} else { } else {
this.state.isLogged !== false && isLogged !== false &&
this.setState({ isLogged: false, account: '' }) this.setState({ isLogged: false, account: '' })
} }
} else {
this.state.isWeb3 !== false &&
this.setState({
isWeb3: false,
isLogged: false
})
} }
} }
private fetchNetwork = async () => { private fetchNetwork = async () => {
const { web3 } = window const { ocean, isWeb3 } = this.state
const { ocean } = this.state
if (web3) { if (isWeb3) {
const network = await ocean.keeper.getNetworkName() const network = await ocean.keeper.getNetworkName()
const isNile = network === 'Nile' const isNile = network === 'Nile'
@ -249,32 +243,6 @@ class App extends Component<{}, AppState> {
} }
} }
private startLoginProcess = async () => {
try {
if (this.state.isWeb3 && window.ethereum) {
await window.ethereum.enable()
const accounts = await this.state.ocean.accounts.list()
if (accounts.length > 0) {
const balance = await accounts[0].getBalance()
this.setState({
isLogged: true,
balance,
account: accounts[0].getId()
})
} else {
// not unlocked
}
} else {
// no metamask/mist, show installation guide!
}
} catch (e) {
Logger.log('error logging', e)
// error in logging process
// show error
// rerun bootstrap process?
}
}
public render() { public render() {
return ( return (
<div className={styles.app}> <div className={styles.app}>

View File

@ -4,7 +4,6 @@ import { Logger } from '@oceanprotocol/squid'
import { User } from '../../context/User' import { User } from '../../context/User'
import Spinner from '../atoms/Spinner' import Spinner from '../atoms/Spinner'
import Asset from '../molecules/Asset' import Asset from '../molecules/Asset'
import Web3message from './Web3message'
import styles from './AssetsUser.module.scss' import styles from './AssetsUser.module.scss'
export default class AssetsUser extends PureComponent< export default class AssetsUser extends PureComponent<
@ -60,48 +59,49 @@ export default class AssetsUser extends PureComponent<
public render() { public render() {
const { account, isNile } = this.context const { account, isNile } = this.context
return isNile && account ? ( return (
<div className={styles.assetsUser}> isNile &&
{this.props.recent && ( account && (
<h2 className={styles.subTitle}> <div className={styles.assetsUser}>
Your Latest Published Data Sets {this.props.recent && (
</h2> <h2 className={styles.subTitle}>
)} Your Latest Published Data Sets
</h2>
)}
{this.state.isLoading ? ( {this.state.isLoading ? (
<Spinner /> <Spinner />
) : this.state.results.length ? ( ) : this.state.results.length ? (
<> <>
{this.state.results {this.state.results
.slice( .slice(
0, 0,
this.props.recent this.props.recent
? this.props.recent ? this.props.recent
: undefined : undefined
) )
.filter(asset => !!asset) .filter(asset => !!asset)
.map((asset: any) => ( .map((asset: any) => (
<Asset <Asset
list={this.props.list} list={this.props.list}
key={asset.id} key={asset.id}
asset={asset} asset={asset}
/> />
))} ))}
{this.props.recent && ( {this.props.recent && (
<Link className={styles.link} to={'/history'}> <Link className={styles.link} to={'/history'}>
All Data Sets All Data Sets
</Link> </Link>
)} )}
</> </>
) : ( ) : (
<div className={styles.empty}> <div className={styles.empty}>
<p>No Data Sets Yet.</p> <p>No Data Sets Yet.</p>
<Link to="/publish">+ Publish A Data Set</Link> <Link to="/publish">+ Publish A Data Set</Link>
</div> </div>
)} )}
</div> </div>
) : ( )
<Web3message />
) )
} }
} }

View File

@ -9,7 +9,7 @@ export default class Web3message extends PureComponent {
private noWeb3 = () => ( private noWeb3 = () => (
<div className={styles.message}> <div className={styles.message}>
<AccountStatus className={styles.status} /> Not a Web3 Browser. For <AccountStatus className={styles.status} /> Not a Web3 Browser. For
publishing or downloading an asset you need to{' '} publishing and downloading an asset you need to{' '}
<a <a
href="https://docs.oceanprotocol.com/tutorials/metamask-setup/" href="https://docs.oceanprotocol.com/tutorials/metamask-setup/"
target="_blank" target="_blank"
@ -21,14 +21,11 @@ export default class Web3message extends PureComponent {
</div> </div>
) )
private unlockAccount = (states: any) => ( private unlockAccount = () => (
<div className={styles.message}> <div className={styles.message}>
<AccountStatus className={styles.status} /> Account locked. For <AccountStatus className={styles.status} /> No accounts detected.
publishing and downloading an asset you need to unlock your Web3 For publishing and downloading an asset you need to unlock your Web3
account.{' '} account.
<Button link onClick={states.startLogin}>
Unlock account
</Button>
</div> </div>
) )
@ -52,20 +49,18 @@ export default class Web3message extends PureComponent {
) )
public render() { public render() {
return ( const { isWeb3, isNile, isLogged, network, account } = this.context
<User.Consumer>
{states => return !isWeb3
!states.isWeb3 ? this.noWeb3()
? this.noWeb3() : !isNile
: !states.isNile ? this.wrongNetwork(network)
? this.wrongNetwork(states.network) : !isLogged
: !states.isLogged ? this.unlockAccount()
? this.unlockAccount(states) : isLogged
: states.isLogged ? this.haveAccount(account)
? this.haveAccount(states.account) : null
: null
}
</User.Consumer>
)
} }
} }
Web3message.contextType = User

View File

@ -13,9 +13,6 @@ export const User = React.createContext({
ocn: 0 ocn: 0
}, },
network: '', network: '',
startLogin: () => {
/* empty */
},
requestFromFaucet: () => { requestFromFaucet: () => {
/* empty */ /* empty */
} }

View File

@ -76,7 +76,7 @@ export default class AssetFile extends PureComponent<
public render() { public render() {
const { ddo, file } = this.props const { ddo, file } = this.props
const { isLoading, message, error } = this.state const { isLoading, message, error } = this.state
const { isLogged } = this.context const { isLogged, isNile } = this.context
return ( return (
<div className={styles.fileWrap}> <div className={styles.fileWrap}>
@ -98,7 +98,7 @@ export default class AssetFile extends PureComponent<
primary primary
className={styles.buttonMain} className={styles.buttonMain}
onClick={() => this.purchaseAsset(ddo, file.index)} onClick={() => this.purchaseAsset(ddo, file.index)}
disabled={!isLogged} disabled={!isLogged || !isNile}
> >
Get file Get file
</Button> </Button>

View File

@ -77,7 +77,7 @@ export default class Faucet extends PureComponent<{}, FaucetState> {
<Button <Button
primary primary
onClick={() => this.getTokens(this.context.requestFromFaucet)} onClick={() => this.getTokens(this.context.requestFromFaucet)}
disabled={!this.context.isLogged} disabled={!this.context.isLogged || !this.context.isNile}
> >
Request Ether Request Ether
</Button> </Button>

View File

@ -1,13 +1,21 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import Route from '../components/templates/Route' import Route from '../components/templates/Route'
import AssetsUser from '../components/organisms/AssetsUser' import AssetsUser from '../components/organisms/AssetsUser'
import Web3message from '../components/organisms/Web3message'
import { User } from '../context/User'
export default class History extends Component { export default class History extends Component {
public render() { public render() {
return ( return (
<Route title="History"> <Route title="History">
{(!this.context.isLogged || !this.context.isNile) && (
<Web3message />
)}
<AssetsUser list /> <AssetsUser list />
</Route> </Route>
) )
} }
} }
History.contextType = User

View File

@ -154,29 +154,9 @@ export default class Step extends PureComponent<StepProps, {}> {
{this.nextButton()} {this.nextButton()}
{lastStep && ( {lastStep && (
<User.Consumer> <Button disabled={!this.context.isLogged} primary>
{states => Register asset
states.isLogged ? ( </Button>
<Button primary>Register asset</Button>
) : states.isWeb3 ? (
<Button onClick={states.startLogin}>
Register asset (unlock Metamask)
</Button>
) : (
<Button
onClick={(e: Event) => {
e.preventDefault()
window.open(
'https://docs.oceanprotocol.com/tutorials/metamask-setup/',
'_blank'
)
}}
>
Register asset (install Metamask)
</Button>
)
}
</User.Consumer>
)} )}
</div> </div>
</> </>

View File

@ -4,6 +4,7 @@ import Route from '../../components/templates/Route'
import Form from '../../components/atoms/Form/Form' import Form from '../../components/atoms/Form/Form'
import AssetModel from '../../models/AssetModel' import AssetModel from '../../models/AssetModel'
import { User } from '../../context/User' import { User } from '../../context/User'
import Web3message from '../../components/organisms/Web3message'
import Step from './Step' import Step from './Step'
import Progress from './Progress' import Progress from './Progress'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
@ -318,6 +319,10 @@ class Publish extends Component<{}, PublishState> {
title="Publish" title="Publish"
description="Publish a new data set into the Ocean Protocol Network." description="Publish a new data set into the Ocean Protocol Network."
> >
{(!this.context.isLogged || !this.context.isNile) && (
<Web3message />
)}
<Progress steps={steps} currentStep={this.state.currentStep} /> <Progress steps={steps} currentStep={this.state.currentStep} />
<Form onSubmit={this.registerAsset}> <Form onSubmit={this.registerAsset}>