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

Merge pull request #15 from oceanprotocol/feature/states-flows

Feature/states flows
This commit is contained in:
Jernej Pregelj 2019-02-13 14:37:57 +01:00 committed by GitHub
commit ce9a362b4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 261 additions and 103 deletions

View File

@ -12,3 +12,16 @@
.main {
flex: 1;
}
.loader {
text-align: center;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin-top: 25vh;
> div {
width: 100%;
}
}

View File

@ -3,6 +3,7 @@ import Web3 from 'web3'
import { BrowserRouter as Router } from 'react-router-dom'
import Header from './components/Header'
import Footer from './components/Footer'
import Spinner from './components/atoms/Spinner'
import { User } from './context/User'
import { provideOcean } from './ocean'
import Routes from './Routes'
@ -21,6 +22,8 @@ declare global {
interface AppState {
isLogged: boolean
isLoading: boolean
isWeb3: boolean
account: string
web3: Web3
ocean: {}
startLogin: () => void
@ -37,11 +40,13 @@ class App extends Component<{}, AppState> {
public state = {
isLogged: false,
isLoading: true,
isWeb3: false,
web3: new Web3(
new Web3.providers.HttpProvider(
`${nodeScheme}://${nodeHost}:${nodePort}`
)
),
account: '',
ocean: {},
startLogin: this.startLogin
}
@ -51,7 +56,6 @@ class App extends Component<{}, AppState> {
}
private startLoginProcess = async () => {
this.setState({ isLoading: true })
if (window.web3) {
const web3 = new Web3(window.web3.currentProvider)
try {
@ -59,15 +63,26 @@ class App extends Component<{}, AppState> {
if (accounts.length > 0) {
this.setState({
isLogged: true,
isWeb3: true,
account: accounts[0],
web3
})
} else {
if (accounts.length === 0 && window.ethereum) {
await window.ethereum.enable()
this.setState({
isLogged: true,
web3
})
const newAccounts = await web3.eth.getAccounts()
if (newAccounts.length > 0) {
this.setState({
isLogged: true,
isWeb3: true,
account: newAccounts[0],
web3
})
} else {
// failed to unlock
}
} else {
// no unlock procedure
}
}
} catch (e) {
@ -76,17 +91,18 @@ class App extends Component<{}, AppState> {
} else {
// no metamask/mist, show installation guide!
}
this.setState({ isLoading: false })
}
private bootstrap = async () => {
if (window.web3) {
this.setState({ isWeb3: true })
const web3 = new Web3(window.web3.currentProvider)
try {
const accounts = await web3.eth.getAccounts()
if (accounts.length > 0) {
this.setState({
isLogged: true,
account: accounts[0],
web3
})
}
@ -94,11 +110,18 @@ class App extends Component<{}, AppState> {
// continue with default
}
}
const { ocean } = await provideOcean()
this.setState({
isLoading: false,
ocean
})
try {
const { ocean } = await provideOcean()
this.setState({
isLoading: false,
ocean
})
} catch (e) {
// show loading error / unable to initialize ocean
this.setState({
isLoading: false
})
}
}
public render() {
@ -110,7 +133,13 @@ class App extends Component<{}, AppState> {
<Header />
<main className={styles.main}>
<Routes />
{this.state.isLoading ? (
<div className={styles.loader}>
<Spinner message="Connecting to Ocean..." />
</div>
) : (
<Routes />
)}
</main>
<Footer />

View File

@ -2,9 +2,13 @@
.message {
margin-bottom: $spacer;
color: $brand-grey-light;
color: $brand-grey;
padding-left: 1.5rem;
position: relative;
border-bottom: .1rem solid $brand-grey-lighter;
border-top: .1rem solid $brand-grey-lighter;
padding-top: $spacer / 2;
padding-bottom: $spacer / 2;
}
// default: red square
@ -16,7 +20,7 @@
margin-right: $spacer / 8;
position: absolute;
left: 0;
top: .3rem;
top: ($spacer / 2) + .3rem;
}
// yellow triangle
@ -38,7 +42,7 @@
}
.account {
font-family: $font-family-monospace;
display: inline-block;
margin-left: $spacer / 8;
background: none;
}

View File

@ -1,48 +1,62 @@
import React, { PureComponent } from 'react'
import Button from '../components/atoms/Button'
import styles from './Web3message.module.scss'
import { User } from '../context/User'
export default class Web3message extends PureComponent {
public render() {
// let indicatorClasses = styles.indicatorCloseEnough
// if (this.props.activeAccount) {
// indicatorClasses = styles.indicatorActive
// }
return (
<>
{/* IF no Web3 */}
<div className={styles.message}>
<span className={styles.indicator} /> No Web3 Browser. For
publishing an asset you need to use a Web3-capable plugin or
browser, like{' '}
<a href="https://docs.oceanprotocol.com/tutorials/wallets/#how-to-setup-metamask">
MetaMask
</a>
.
</div>
{/* IF connected and account locked */}
<div className={styles.message}>
<span className={styles.indicatorCloseEnough} /> Account
locked. For publishing an asset you need to unlock your Web3
account.
<Button link>Unlock account</Button>
</div>
{/* IF connected and unlocked */}
<div className={styles.message}>
<span className={styles.indicatorActive} /> Connected with
account
<span
className={styles.account}
title="0xfehz2u89nfewhji432ntio43huof42huifewhnuefwo"
>
0xfehz2u89n...
</span>
</div>
<User.Consumer>
{states =>
!states.isWeb3
? this.noWeb3()
: !states.isLogged
? this.unlockAccount(states)
: states.isLogged
? this.haveAccount(states.account)
: null
}
</User.Consumer>
</>
)
}
public noWeb3() {
return (
<div className={styles.message}>
<span className={styles.indicator} /> No Web3 Browser. For
publishing an asset you need to use a Web3-capable plugin or
browser, like{' '}
<a href="https://docs.oceanprotocol.com/tutorials/wallets/#how-to-setup-metamask">
MetaMask
</a>
.
</div>
)
}
public unlockAccount(states: any) {
return (
<div className={styles.message}>
<span className={styles.indicatorCloseEnough} /> Account locked.
For publishing an asset you need to unlock your Web3 account.
<Button link onClick={states.startLogin}>
Unlock account
</Button>
</div>
)
}
public haveAccount(account: string) {
return (
<div className={styles.message}>
<span className={styles.indicatorActive} /> Connected with
account
<code className={styles.account} title={account && account}>
{`${account && account.substring(0, 20)}...`}
</code>
</div>
)
}
}

View File

@ -22,6 +22,11 @@
}
}
.spinnerMessage {
color: $brand-grey-light;
margin-top: $spacer / 2;
}
@keyframes spinner {
to {
transform: rotate(360deg);

View File

@ -1,6 +1,10 @@
import React from 'react'
import styles from './Spinner.module.scss'
const Spinner = () => <div className={styles.spinner} />
const Spinner = ({ message }: { message?: string }) => (
<div className={styles.spinner}>
{message && <div className={styles.spinnerMessage}>{message}</div>}
</div>
)
export default Spinner

View File

@ -3,6 +3,8 @@ import React from 'react'
export const User = React.createContext({
isLogged: false,
isLoading: false,
isWeb3: false,
account: '',
web3: {},
ocean: {},
startLogin: () => {

View File

@ -3,6 +3,7 @@ import React, { Component } from 'react'
import Route from '../components/templates/Route'
import Button from '../components/atoms/Button'
import { User } from '../context/User'
import quertString from 'query-string'
interface DetailsState {
ddo: any
@ -26,29 +27,33 @@ export default class Details extends Component<DetailsProps, DetailsState> {
}
private purchaseAsset = async (ddo: any) => {
const account = await this.context.ocean.getAccounts()
const service = ddo.findServiceByType('Access')
const serviceAgreementSignatureResult: any = await this.context.ocean.signServiceAgreement(
ddo.id,
service.serviceDefinitionId,
account[0]
)
Logger.log(
'serviceAgreementSignatureResult',
serviceAgreementSignatureResult
)
await this.context.ocean.initializeServiceAgreement(
ddo.id,
service.serviceDefinitionId,
serviceAgreementSignatureResult.serviceAgreementId,
serviceAgreementSignatureResult.serviceAgreementSignature,
(files: any) =>
Logger.log(
`Got files, first files length in bytes: ${files[0].length}`
),
account[0]
)
try {
const account = await this.context.ocean.getAccounts()
const service = ddo.findServiceByType('Access')
const serviceAgreementSignatureResult: any = await this.context.ocean.signServiceAgreement(
ddo.id,
service.serviceDefinitionId,
account[0]
)
await this.context.ocean.initializeServiceAgreement(
ddo.id,
service.serviceDefinitionId,
serviceAgreementSignatureResult.serviceAgreementId,
serviceAgreementSignatureResult.serviceAgreementSignature,
(files: any) => {
files.forEach((file: any) => {
const parsedUrl: any = quertString.parseUrl(file)
setTimeout(() => {
// eslint-disable-next-line
window.open(parsedUrl.query.url)
}, 100)
})
},
account[0]
)
} catch (e) {
Logger.log('error', e)
}
}
private showDetails = (ddo: any) => {
@ -56,7 +61,7 @@ export default class Details extends Component<DetailsProps, DetailsState> {
<>
<div>{JSON.stringify(this.state.metadata)}</div>
<Button onClick={this.purchaseAsset(ddo)}>
<Button onClick={() => this.purchaseAsset(ddo)}>
Purchase asset
</Button>
</>

View File

@ -1,4 +1,5 @@
import React, { ChangeEvent, Component, FormEvent } from 'react'
import { Logger } from '@oceanprotocol/squid'
import Route from '../components/templates/Route'
import Button from '../components/atoms/Button'
import Form from '../components/atoms/Form/Form'
@ -23,6 +24,10 @@ interface PublishState {
copyrightHolder?: string
categories?: string[]
tags?: string[]
isPublishing?: boolean
isPublished?: boolean
publishedDid?: string
publishingError?: string
}
class Publish extends Component<{}, PublishState> {
@ -36,7 +41,11 @@ class Publish extends Component<{}, PublishState> {
type: 'dataset' as AssetType,
license: '',
copyrightHolder: '',
categories: ['']
categories: [''],
isPublishing: false,
isPublished: false,
publishedDid: '',
publishingError: ''
}
public formFields = (entries: any[]) =>
@ -80,8 +89,33 @@ class Publish extends Component<{}, PublishState> {
})
}
private tryAgain = () => {
this.setState({ publishingError: '' })
}
private toStart = () => {
this.setState({
name: '',
dateCreated: new Date(),
description: '',
files: [''],
price: 0,
author: '',
type: 'dataset' as AssetType,
license: '',
copyrightHolder: '',
categories: [''],
isPublishing: false,
isPublished: false
})
}
private registerAsset = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
this.setState({
publishingError: '',
isPublishing: true
})
const account = await this.context.ocean.getAccounts()
const newAsset = {
// OEP-08 Attributes
@ -109,8 +143,26 @@ class Publish extends Component<{}, PublishState> {
AssetModel.additionalInformation
)
}
await this.context.ocean.registerAsset(newAsset, account[0])
try {
const asset = await this.context.ocean.registerAsset(
newAsset,
account[0]
)
Logger.log('asset:', asset)
this.setState({
publishedDid: asset.id,
isPublished: true
})
} catch (e) {
// make readable errors
Logger.log('error:', e)
this.setState({
publishingError: e
})
}
this.setState({
isPublishing: false
})
}
public render() {
@ -120,30 +172,60 @@ class Publish extends Component<{}, PublishState> {
<Route title="Publish">
<Web3message />
<Form
title={form.title}
description={form.description}
onSubmit={this.registerAsset}
>
{this.formFields(entries)}
{this.state.isPublishing ? (
this.publishingState()
) : this.state.publishingError ? (
this.errorState()
) : this.state.isPublished ? (
this.publishedState()
) : (
<Form
title={form.title}
description={form.description}
onSubmit={this.registerAsset}
>
{this.formFields(entries)}
<User.Consumer>
{states =>
states.isLogged ? (
<Button primary>
Register asset (we are logged)
</Button>
) : (
<Button primary onClick={states.startLogin}>
Register asset (login first)
</Button>
)
}
</User.Consumer>
</Form>
<User.Consumer>
{states =>
states.isLogged ? (
<Button primary>Register asset</Button>
) : (
<Button onClick={states.startLogin}>
Register asset (login first)
</Button>
)
}
</User.Consumer>
</Form>
)}
</Route>
)
}
public publishingState = () => {
return <div>Please sign with your crypto wallet</div>
}
public errorState = () => {
return (
<div>
Something went wrong,{' '}
<a onClick={() => this.tryAgain()}>try again</a>
</div>
)
}
public publishedState = () => {
return (
<div>
Your asset is published! See it{' '}
<a href={'/asset/' + this.state.publishedDid}>here</a>, submit
another asset by clicking{' '}
<a onClick={() => this.toStart()}>here</a>
</div>
)
}
}
Publish.contextType = User

View File

@ -2,7 +2,7 @@ import queryString from 'query-string'
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import Route from '../components/templates/Route'
import { provideOcean } from '../ocean'
import { User } from '../context/User'
interface SearchState {
results: any[]
@ -17,8 +17,6 @@ export default class Search extends Component<SearchProps, SearchState> {
public state = { results: [] }
public async componentDidMount() {
// temporary ocean init and asset retrieval
const { ocean } = await provideOcean()
const searchParams = queryString.parse(this.props.location.search)
const queryRequest: any = {
offset: 100,
@ -29,7 +27,7 @@ export default class Search extends Component<SearchProps, SearchState> {
}
}
}
const assets = await ocean.searchAssets(queryRequest)
const assets = await this.context.ocean.searchAssets(queryRequest)
this.setState({ results: assets })
}
@ -56,3 +54,5 @@ export default class Search extends Component<SearchProps, SearchState> {
)
}
}
Search.contextType = User