mirror of
https://github.com/oceanprotocol/commons.git
synced 2023-03-15 18:03:00 +01:00
Merge pull request #25 from oceanprotocol/feature/account-popup
Account & Faucet UI
This commit is contained in:
commit
214c5358eb
105
package-lock.json
generated
105
package-lock.json
generated
@ -2402,8 +2402,7 @@
|
||||
"asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
|
||||
"dev": true
|
||||
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
@ -5048,6 +5047,15 @@
|
||||
"sha.js": "^2.4.8"
|
||||
}
|
||||
},
|
||||
"create-react-context": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.2.tgz",
|
||||
"integrity": "sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A==",
|
||||
"requires": {
|
||||
"fbjs": "^0.8.0",
|
||||
"gud": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||
@ -6274,6 +6282,14 @@
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||
},
|
||||
"encoding": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
||||
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
|
||||
"requires": {
|
||||
"iconv-lite": "~0.4.13"
|
||||
}
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
|
||||
@ -7474,6 +7490,35 @@
|
||||
"bser": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fbjs": {
|
||||
"version": "0.8.17",
|
||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
|
||||
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
|
||||
"requires": {
|
||||
"core-js": "^1.0.0",
|
||||
"isomorphic-fetch": "^2.1.1",
|
||||
"loose-envify": "^1.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"promise": "^7.1.1",
|
||||
"setimmediate": "^1.0.5",
|
||||
"ua-parser-js": "^0.7.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
||||
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
||||
"requires": {
|
||||
"asap": "~2.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fd-slicer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
|
||||
@ -8982,6 +9027,11 @@
|
||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
|
||||
"dev": true
|
||||
},
|
||||
"gud": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||
},
|
||||
"gzip-size": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz",
|
||||
@ -10476,6 +10526,26 @@
|
||||
"isarray": "1.0.0"
|
||||
}
|
||||
},
|
||||
"isomorphic-fetch": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
|
||||
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
|
||||
"requires": {
|
||||
"node-fetch": "^1.0.1",
|
||||
"whatwg-fetch": ">=0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
|
||||
"requires": {
|
||||
"encoding": "^0.1.11",
|
||||
"is-stream": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
@ -13404,6 +13474,11 @@
|
||||
"ts-pnp": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"popper.js": {
|
||||
"version": "1.14.7",
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.7.tgz",
|
||||
"integrity": "sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ=="
|
||||
},
|
||||
"portfinder": {
|
||||
"version": "1.0.20",
|
||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
|
||||
@ -17233,6 +17308,19 @@
|
||||
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.8.4.tgz",
|
||||
"integrity": "sha512-QhI19OcfhiAn60/O6bMR0w8ApXrPFCjv6+eV0I/P9/AswzjgEAx4L7VxMBCpS/jrythLa12Q9v88req+ys4YpA=="
|
||||
},
|
||||
"react-popper": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.3.tgz",
|
||||
"integrity": "sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"create-react-context": "<=0.2.2",
|
||||
"popper.js": "^1.14.4",
|
||||
"prop-types": "^15.6.1",
|
||||
"typed-styles": "^0.0.7",
|
||||
"warning": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
|
||||
@ -21343,6 +21431,11 @@
|
||||
"mime-types": "~2.1.18"
|
||||
}
|
||||
},
|
||||
"typed-styles": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
|
||||
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
|
||||
},
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
@ -21363,6 +21456,11 @@
|
||||
"integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==",
|
||||
"dev": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.19",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
|
||||
"integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ=="
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.9",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
|
||||
@ -23384,8 +23482,7 @@
|
||||
"whatwg-fetch": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
|
||||
"integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
|
||||
},
|
||||
"whatwg-mimetype": {
|
||||
"version": "2.3.0",
|
||||
|
@ -25,6 +25,7 @@
|
||||
"react-dom": "^16.8.3",
|
||||
"react-helmet": "^5.2.0",
|
||||
"react-moment": "^0.8.4",
|
||||
"react-popper": "^1.3.3",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-transition-group": "^2.6.0",
|
||||
"slugify": "^1.3.4",
|
||||
|
22
src/App.tsx
22
src/App.tsx
@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react'
|
||||
import Web3 from 'web3'
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
import { Logger } from '@oceanprotocol/squid'
|
||||
import Header from './components/Header'
|
||||
import Footer from './components/Footer'
|
||||
import Spinner from './components/atoms/Spinner'
|
||||
@ -31,6 +32,11 @@ interface AppState {
|
||||
isLoading: boolean
|
||||
isWeb3: boolean
|
||||
account: string
|
||||
balance: {
|
||||
eth: number
|
||||
ocn: number
|
||||
}
|
||||
network: string
|
||||
web3: Web3
|
||||
ocean: {}
|
||||
startLogin: () => void
|
||||
@ -62,7 +68,7 @@ class App extends Component<{}, AppState> {
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
// show error
|
||||
Logger.log('requestFromFaucet', error)
|
||||
}
|
||||
} else {
|
||||
// no account found
|
||||
@ -73,6 +79,11 @@ class App extends Component<{}, AppState> {
|
||||
isLogged: false,
|
||||
isLoading: true,
|
||||
isWeb3: false,
|
||||
balance: {
|
||||
eth: 0,
|
||||
ocn: 0
|
||||
},
|
||||
network: '',
|
||||
web3: new Web3(
|
||||
new Web3.providers.HttpProvider(
|
||||
`${nodeScheme}://${nodeHost}:${nodePort}`
|
||||
@ -140,7 +151,7 @@ class App extends Component<{}, AppState> {
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
// continue with default
|
||||
Logger.log('web3 error', e)
|
||||
}
|
||||
}
|
||||
try {
|
||||
@ -149,8 +160,13 @@ class App extends Component<{}, AppState> {
|
||||
isLoading: false,
|
||||
ocean
|
||||
})
|
||||
// TODO: squid-js balance retrieval fix
|
||||
const accounts = await ocean.getAccounts()
|
||||
const balance = await accounts[0].getBalance()
|
||||
this.setState({ balance })
|
||||
// TODO: squid-js expose keeper for getNetworkName
|
||||
} catch (e) {
|
||||
// show loading error / unable to initialize ocean
|
||||
Logger.log('ocean/balance error', e)
|
||||
this.setState({
|
||||
isLoading: false
|
||||
})
|
||||
|
@ -7,6 +7,7 @@ import Home from './routes/Home'
|
||||
import NotFound from './routes/NotFound'
|
||||
import Publish from './routes/Publish/'
|
||||
import Search from './routes/Search'
|
||||
import Faucet from './routes/Faucet'
|
||||
import Styleguide from './routes/Styleguide'
|
||||
|
||||
const Routes = () => (
|
||||
@ -17,6 +18,7 @@ const Routes = () => (
|
||||
<Route component={Publish} path="/publish" />
|
||||
<Route component={Search} path="/search" />
|
||||
<Route component={Details} path="/asset/:did" />
|
||||
<Route component={Faucet} path="/faucet" />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
)
|
||||
|
@ -1,9 +1,6 @@
|
||||
@import '../styles/variables';
|
||||
|
||||
.header {
|
||||
// background: $brand-black
|
||||
// url('@oceanprotocol/art/mantaray/mantaray-back.svg') no-repeat center -6rem;
|
||||
// background-size: cover;
|
||||
width: 100%;
|
||||
padding: $spacer / 2 0;
|
||||
}
|
||||
@ -94,3 +91,8 @@
|
||||
color: $brand-pink;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.accountStatus {
|
||||
margin-left: $spacer;
|
||||
margin-bottom: .2rem;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { ReactComponent as Logo } from '@oceanprotocol/art/logo/logo.svg'
|
||||
import AccountStatus from './molecules/AccountStatus/'
|
||||
import styles from './Header.module.scss'
|
||||
|
||||
import menu from '../data/menu.json'
|
||||
@ -27,6 +28,7 @@ const Header = () => (
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
<AccountStatus className={styles.accountStatus} />
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
|
@ -1,48 +0,0 @@
|
||||
@import '../styles/variables';
|
||||
|
||||
.message {
|
||||
margin-bottom: $spacer;
|
||||
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
|
||||
.indicator {
|
||||
display: inline-block;
|
||||
width: $font-size-small;
|
||||
height: $font-size-small;
|
||||
background: $red;
|
||||
margin-right: $spacer / 8;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: ($spacer / 2) + .3rem;
|
||||
}
|
||||
|
||||
// yellow triangle
|
||||
.indicatorCloseEnough {
|
||||
composes: indicator;
|
||||
background: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: $font-size-small / 1.75 solid transparent;
|
||||
border-right: $font-size-small / 1.75 solid transparent;
|
||||
border-bottom: $font-size-small solid $yellow;
|
||||
}
|
||||
|
||||
// green circle
|
||||
.indicatorActive {
|
||||
composes: indicator;
|
||||
border-radius: 50%;
|
||||
background: $green;
|
||||
}
|
||||
|
||||
.account {
|
||||
display: inline-block;
|
||||
margin-left: $spacer / 8;
|
||||
background: none;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
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() {
|
||||
return (
|
||||
<>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
}
|
@ -3,8 +3,8 @@
|
||||
.spinner {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin-bottom: $spacer;
|
||||
margin-top: $spacer * 2;
|
||||
margin-top: $spacer * $line-height;
|
||||
margin-bottom: $spacer / 2;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
|
33
src/components/molecules/AccountStatus/Indicator.module.scss
Normal file
33
src/components/molecules/AccountStatus/Indicator.module.scss
Normal file
@ -0,0 +1,33 @@
|
||||
@import '../../../styles/variables';
|
||||
|
||||
.status {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
// default: red square
|
||||
.statusIndicator {
|
||||
width: $font-size-small;
|
||||
height: $font-size-small;
|
||||
display: block;
|
||||
background: $red;
|
||||
}
|
||||
|
||||
// yellow triangle
|
||||
.statusIndicatorCloseEnough {
|
||||
composes: statusIndicator;
|
||||
background: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: $font-size-small / 1.7 solid transparent;
|
||||
border-right: $font-size-small / 1.7 solid transparent;
|
||||
border-bottom: $font-size-small solid $yellow;
|
||||
}
|
||||
|
||||
// green circle
|
||||
.statusIndicatorActive {
|
||||
composes: statusIndicator;
|
||||
border-radius: 50%;
|
||||
background: $green;
|
||||
}
|
35
src/components/molecules/AccountStatus/Indicator.tsx
Normal file
35
src/components/molecules/AccountStatus/Indicator.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react'
|
||||
import cx from 'classnames'
|
||||
import { User } from '../../../context/User'
|
||||
import styles from './Indicator.module.scss'
|
||||
|
||||
const Indicator = ({
|
||||
className,
|
||||
togglePopover,
|
||||
forwardedRef
|
||||
}: {
|
||||
className?: string
|
||||
togglePopover: () => void
|
||||
forwardedRef: (ref: HTMLElement | null) => void
|
||||
}) => (
|
||||
<div
|
||||
className={cx(styles.status, className)}
|
||||
onMouseOver={togglePopover}
|
||||
onMouseOut={togglePopover}
|
||||
ref={forwardedRef}
|
||||
>
|
||||
<User.Consumer>
|
||||
{states =>
|
||||
!states.isWeb3 ? (
|
||||
<span className={styles.statusIndicator} />
|
||||
) : !states.isLogged ? (
|
||||
<span className={styles.statusIndicatorCloseEnough} />
|
||||
) : states.isLogged ? (
|
||||
<span className={styles.statusIndicatorActive} />
|
||||
) : null
|
||||
}
|
||||
</User.Consumer>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Indicator
|
62
src/components/molecules/AccountStatus/Popover.module.scss
Normal file
62
src/components/molecules/AccountStatus/Popover.module.scss
Normal file
@ -0,0 +1,62 @@
|
||||
@import '../../../styles/variables';
|
||||
|
||||
$popoverWidth: 18rem;
|
||||
|
||||
.popover {
|
||||
position: relative;
|
||||
width: $popoverWidth;
|
||||
padding: $spacer / 2;
|
||||
background: $brand-black;
|
||||
border-radius: .1rem;
|
||||
border: .1rem solid $brand-grey-light;
|
||||
box-shadow: 0 6px 16px 0 rgba($brand-black, .3);
|
||||
color: $brand-grey-light;
|
||||
font-size: $font-size-small;
|
||||
animation: showPopup .2s ease-in forwards;
|
||||
}
|
||||
|
||||
@keyframes showPopup {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.popoverInfoline {
|
||||
border-bottom: .05rem solid $brand-grey;
|
||||
padding: $spacer / 3 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
width: 15rem;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.balance {
|
||||
font-size: $font-size-small;
|
||||
margin-left: $spacer / 2;
|
||||
white-space: nowrap;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
59
src/components/molecules/AccountStatus/Popover.tsx
Normal file
59
src/components/molecules/AccountStatus/Popover.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React from 'react'
|
||||
import { User } from '../../../context/User'
|
||||
import styles from './Popover.module.scss'
|
||||
|
||||
const Popover = ({
|
||||
forwardedRef,
|
||||
style
|
||||
}: {
|
||||
forwardedRef: (ref: HTMLElement | null) => void
|
||||
style: React.CSSProperties
|
||||
}) => (
|
||||
<div className={styles.popover} ref={forwardedRef} style={style}>
|
||||
<User.Consumer>
|
||||
{states =>
|
||||
states.account &&
|
||||
states.balance && (
|
||||
<div className={styles.popoverInfoline}>
|
||||
<span
|
||||
className={styles.balance}
|
||||
title={(states.balance.eth / 1e18).toFixed(10)}
|
||||
>
|
||||
<strong>
|
||||
{(states.balance.eth / 1e18)
|
||||
.toFixed(3)
|
||||
.slice(0, -1)}
|
||||
</strong>{' '}
|
||||
ETH
|
||||
</span>
|
||||
<span className={styles.balance}>
|
||||
<strong>{states.balance.ocn}</strong> OCEAN
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</User.Consumer>
|
||||
|
||||
<div className={styles.popoverInfoline}>
|
||||
<User.Consumer>
|
||||
{states =>
|
||||
states.account ? (
|
||||
<span className={styles.address} title={states.account}>
|
||||
{states.account}
|
||||
</span>
|
||||
) : (
|
||||
<em>No account selected</em>
|
||||
)
|
||||
}
|
||||
</User.Consumer>
|
||||
</div>
|
||||
|
||||
<div className={styles.popoverInfoline}>
|
||||
<User.Consumer>
|
||||
{states => states.network && states.network}
|
||||
</User.Consumer>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Popover
|
54
src/components/molecules/AccountStatus/index.tsx
Normal file
54
src/components/molecules/AccountStatus/index.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import { Manager, Reference, Popper } from 'react-popper'
|
||||
import AccountPopover from './Popover'
|
||||
import AccountIndicator from './Indicator'
|
||||
|
||||
interface AccountStatusProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface AccountStatusState {
|
||||
isPopoverOpen: boolean
|
||||
}
|
||||
|
||||
export default class AccountStatus extends PureComponent<
|
||||
AccountStatusProps,
|
||||
AccountStatusState
|
||||
> {
|
||||
public state = {
|
||||
isPopoverOpen: false
|
||||
}
|
||||
|
||||
public togglePopover() {
|
||||
this.setState(prevState => ({
|
||||
isPopoverOpen: !prevState.isPopoverOpen
|
||||
}))
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Manager>
|
||||
<Reference>
|
||||
{({ ref }) => (
|
||||
<AccountIndicator
|
||||
togglePopover={() => this.togglePopover()}
|
||||
className={this.props.className}
|
||||
forwardedRef={ref}
|
||||
/>
|
||||
)}
|
||||
</Reference>
|
||||
{this.state.isPopoverOpen && (
|
||||
<Popper placement="auto">
|
||||
{({ ref, style, placement }) => (
|
||||
<AccountPopover
|
||||
forwardedRef={ref}
|
||||
style={style}
|
||||
data-placement={placement}
|
||||
/>
|
||||
)}
|
||||
</Popper>
|
||||
)}
|
||||
</Manager>
|
||||
)
|
||||
}
|
||||
}
|
23
src/components/molecules/Web3message.module.scss
Normal file
23
src/components/molecules/Web3message.module.scss
Normal file
@ -0,0 +1,23 @@
|
||||
@import '../../styles/variables';
|
||||
|
||||
.message {
|
||||
margin-bottom: $spacer;
|
||||
color: $brand-grey;
|
||||
padding-left: 2rem;
|
||||
position: relative;
|
||||
border-bottom: .1rem solid $brand-grey-lighter;
|
||||
border-top: .1rem solid $brand-grey-lighter;
|
||||
padding-top: $spacer / 2;
|
||||
padding-bottom: $spacer / 2;
|
||||
}
|
||||
|
||||
.account {
|
||||
display: inline-block;
|
||||
margin-left: $spacer / 8;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-left: -($spacer);
|
||||
margin-right: $spacer / 3;
|
||||
}
|
60
src/components/molecules/Web3message.tsx
Normal file
60
src/components/molecules/Web3message.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import Button from '../atoms/Button'
|
||||
import AccountStatus from './AccountStatus/'
|
||||
import styles from './Web3message.module.scss'
|
||||
import { User } from '../../context/User'
|
||||
|
||||
export default class Web3message extends PureComponent {
|
||||
public render() {
|
||||
return (
|
||||
<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}>
|
||||
<AccountStatus className={styles.status} /> No Web3 Browser. For
|
||||
publishing an asset you need to{' '}
|
||||
<a href="https://docs.oceanprotocol.com/tutorials/metamask-setup/">
|
||||
setup MetaMask
|
||||
</a>{' '}
|
||||
or use any other Web3-capable plugin or browser.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
public unlockAccount(states: any) {
|
||||
return (
|
||||
<div className={styles.message}>
|
||||
<AccountStatus className={styles.status} /> 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}>
|
||||
<AccountStatus className={styles.status} /> Connected with
|
||||
account
|
||||
<code className={styles.account} title={account && account}>
|
||||
{`${account && account.substring(0, 20)}...`}
|
||||
</code>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -7,6 +7,11 @@ export const User = React.createContext({
|
||||
account: '',
|
||||
web3: {},
|
||||
ocean: {},
|
||||
balance: {
|
||||
eth: 0,
|
||||
ocn: 0
|
||||
},
|
||||
network: '',
|
||||
startLogin: () => {
|
||||
/* empty */
|
||||
},
|
||||
|
@ -3,6 +3,10 @@
|
||||
"title": "Publish",
|
||||
"link": "/publish"
|
||||
},
|
||||
{
|
||||
"title": "Faucet",
|
||||
"link": "/faucet"
|
||||
},
|
||||
{
|
||||
"title": "About",
|
||||
"link": "/about"
|
||||
|
15
src/routes/Faucet.module.scss
Normal file
15
src/routes/Faucet.module.scss
Normal file
@ -0,0 +1,15 @@
|
||||
@import '../styles/variables';
|
||||
|
||||
.action {
|
||||
text-align: center;
|
||||
margin-top: $spacer * 2;
|
||||
|
||||
p {
|
||||
margin-top: $spacer;
|
||||
color: $brand-grey-light;
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
color: $green;
|
||||
}
|
96
src/routes/Faucet.tsx
Normal file
96
src/routes/Faucet.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import Route from '../components/templates/Route'
|
||||
import Button from '../components/atoms/Button'
|
||||
import Spinner from '../components/atoms/Spinner'
|
||||
import { User } from '../context/User'
|
||||
import Web3message from '../components/molecules/Web3message'
|
||||
import styles from './Faucet.module.scss'
|
||||
|
||||
interface FaucetState {
|
||||
isLoading: boolean
|
||||
success?: string
|
||||
error?: string
|
||||
eth?: string
|
||||
}
|
||||
|
||||
export default class Faucet extends PureComponent<{}, FaucetState> {
|
||||
public state = {
|
||||
isLoading: false,
|
||||
success: undefined,
|
||||
error: undefined,
|
||||
eth: 'xx'
|
||||
}
|
||||
|
||||
private getTokens = async (requestFromFaucet: () => void) => {
|
||||
this.setState({ isLoading: true })
|
||||
|
||||
try {
|
||||
await requestFromFaucet()
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
success: `Successfully added ${
|
||||
this.state.eth
|
||||
} ETH to your account.`
|
||||
})
|
||||
} catch (error) {
|
||||
this.setState({ isLoading: false, error })
|
||||
}
|
||||
}
|
||||
|
||||
private RequestMarkup = () => (
|
||||
<User.Consumer>
|
||||
{states => (
|
||||
<Button
|
||||
primary
|
||||
onClick={() => this.getTokens(states.requestFromFaucet)}
|
||||
>
|
||||
Request Ether
|
||||
</Button>
|
||||
)}
|
||||
</User.Consumer>
|
||||
)
|
||||
|
||||
private ActionMarkup = () => (
|
||||
<div className={styles.action}>
|
||||
{this.state.isLoading ? (
|
||||
<Spinner message="Getting Ether..." />
|
||||
) : this.state.error ? (
|
||||
<div className={styles.error}>
|
||||
{this.state.error}{' '}
|
||||
<Button link onClick={this.reset}>
|
||||
Try again
|
||||
</Button>
|
||||
</div>
|
||||
) : this.state.success ? (
|
||||
<div className={styles.success}>{this.state.success}</div>
|
||||
) : (
|
||||
<this.RequestMarkup />
|
||||
)}
|
||||
|
||||
<p>
|
||||
You can only request Ether once every 24 hours for your address.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
private reset = () => {
|
||||
this.setState({
|
||||
error: undefined,
|
||||
success: undefined,
|
||||
isLoading: false
|
||||
})
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Route
|
||||
title="Faucet"
|
||||
description="Shower yourself with some Ether for the Ocean POA network."
|
||||
>
|
||||
<Web3message />
|
||||
|
||||
<this.ActionMarkup />
|
||||
</Route>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import Web3message from '../../components/Web3message'
|
||||
import Web3message from '../../components/molecules/Web3message'
|
||||
import Spinner from '../../components/atoms/Spinner'
|
||||
import styles from './StepRegisterContent.module.scss'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user