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

Faucet route refactor

* split up components
* move actions state management into single action component
* layout & style tweaks
* update tests
This commit is contained in:
Matthias Kretschmann 2019-10-11 18:25:13 +02:00
parent 8f0988e1b0
commit 5b0c3486dd
Signed by: m
GPG Key ID: 606EEEF3C479A91F
9 changed files with 195 additions and 193 deletions

View File

@ -1,170 +0,0 @@
import React, { PureComponent } from 'react'
import { FaucetResponse } from '../ocean'
import Route from '../components/templates/Route'
import Button from '../components/atoms/Button'
import Spinner from '../components/atoms/Spinner'
import { User, Market } from '../context'
import Web3message from '../components/organisms/Web3message'
import styles from './Faucet.module.scss'
import Content from '../components/atoms/Content'
import withTracker from '../hoc/withTracker'
import { showRequestTokens } from '../config'
interface FaucetState {
isLoading: boolean
success?: string
error?: string
trxHash?: string
}
class Faucet extends PureComponent<{}, FaucetState> {
public static contextType = User
public state = {
isLoading: false,
success: undefined,
error: undefined,
trxHash: undefined
}
private getTokens = async () => {
const { ocean } = this.context
const accounts = await ocean.accounts.list()
const account = accounts[0]
await ocean.accounts.requestTokens(account, 100)
}
private getEther = async (
requestFromFaucet: () => Promise<FaucetResponse>
) => {
this.setState({ isLoading: true })
try {
const response = await requestFromFaucet()
if (!response.success) {
this.setState({
isLoading: false,
error: response.message
})
return
}
const { trxHash } = response
this.setState({
isLoading: false,
success: response.message,
trxHash
})
} catch (error) {
this.setState({ isLoading: false, error: error.message })
}
}
private reset = () => {
this.setState({
error: undefined,
success: undefined,
isLoading: false
})
}
private Success = () => {
const { network } = this.context
const { trxHash } = this.state
const submarineLink = `https://submarine.${
network === 'pacific' ? 'oceanprotocol' : `${network}.dev-ocean`
}.com/tx/${trxHash}`
return (
<div className={styles.success}>
<strong>{this.state.success}</strong>
<p>
<strong>Your Transaction Hash</strong>
<a href={submarineLink}>
<code>{trxHash}</code>
</a>
</p>
</div>
)
}
private Error = () => (
<div className={styles.error}>
<p>{this.state.error}</p>
<Button onClick={() => this.reset()}>Try again</Button>
</div>
)
private GetEther = () => (
<>
<Button
primary
onClick={() => this.getEther(this.context.requestFromFaucet)}
disabled={!this.context.isLogged}
name="FaucetEther"
>
Request Ether
</Button>
<p>
You can only request Ether once every 24 hours for your address.
</p>
</>
)
private GetTokens = () => (
<>
<Button
primary
onClick={() => this.getTokens()}
disabled={!this.context.isLogged}
name="FaucetTokens"
>
Request OCEAN Tokens
</Button>
<p>You can request tokens every once in a while.</p>
</>
)
public render() {
const { isLogged } = this.context
const { isLoading, error, success } = this.state
return (
<Market.Consumer>
{market => (
<Route
title="Faucet"
description={`Shower yourself with some Ether for Ocean's ${market.network} network.`}
>
<Content>
<Web3message />
<div className={styles.action}>
{isLoading ? (
<Spinner message="Getting Ether..." />
) : error ? (
<this.Error />
) : success ? (
<this.Success />
) : (
<>
{isLogged && <this.GetEther />}
{isLogged && showRequestTokens && (
<this.GetTokens />
)}
</>
)}
</div>
</Content>
</Route>
)}
</Market.Consumer>
)
}
}
export default withTracker(Faucet)

View File

@ -0,0 +1,12 @@
@import '../../styles/variables';
.action {
text-align: center;
margin-top: $spacer;
p {
margin-top: $spacer / 2;
color: $brand-grey-light;
font-size: $font-size-small;
}
}

View File

@ -0,0 +1,98 @@
import React, { useContext, useState } from 'react'
import { FaucetResponse } from '../../ocean'
import Button from '../../components/atoms/Button'
import Spinner from '../../components/atoms/Spinner'
import { User } from '../../context'
import styles from './Action.module.scss'
import { Ocean } from '@oceanprotocol/squid'
import { ActionError, ActionSuccess } from './ActionResponse'
const ActionMarkup = ({
token,
handleAction
}: {
token: string
handleAction: () => void
}) => {
const { isLogged } = useContext(User)
return (
<>
<Button
primary
onClick={handleAction}
disabled={!isLogged}
name={`Faucet${token}`}
>
{`Request ${token}`}
</Button>
{token === 'ETH' && (
<p>
You can only request {token} once every 24 hours for your
address.
</p>
)}
</>
)
}
export default function Action({ token }: { token: string }) {
const { ocean, requestFromFaucet } = useContext(User)
const [isLoading, setIsLoading] = useState(false)
const [success, setSuccess] = useState('')
const [error, setError] = useState('')
const [trxHash, setTrxHash] = useState('')
async function getOcean(oceanInstance: Ocean) {
const accounts = await oceanInstance.accounts.list()
const account = accounts[0]
const success = await oceanInstance.accounts.requestTokens(account, 100)
success
? setSuccess('Received 100 Ocean Tokens.')
: setError('Failed getting Ocean Tokens.')
}
async function getEther(requestFromFaucet: () => Promise<FaucetResponse>) {
try {
const response = await requestFromFaucet()
const { message, success } = response
if (!success) {
setError(message)
return
}
const { trxHash } = response
setSuccess(message)
trxHash && setTrxHash(trxHash)
} catch (error) {
setError(error.message)
}
}
const handleAction = async () => {
setIsLoading(true)
token === 'OCEAN'
? await getOcean(ocean as Ocean)
: await getEther(requestFromFaucet as () => Promise<FaucetResponse>)
setIsLoading(false)
}
return (
<div className={styles.action}>
{isLoading ? (
<Spinner message={`Getting ${token}...`} />
) : error ? (
<ActionError error={error} />
) : success ? (
<ActionSuccess success={success} trxHash={trxHash} />
) : (
<ActionMarkup token={token} handleAction={handleAction} />
)}
</div>
)
}

View File

@ -1,21 +1,8 @@
@import '../styles/variables';
.action {
text-align: center;
margin-top: $spacer * 2;
p {
margin-top: $spacer;
color: $brand-grey-light;
}
}
@import '../../styles/variables';
.success {
color: $green;
p {
margin-top: $spacer / 2;
}
margin-top: $spacer / 2;
strong {
display: block;

View File

@ -0,0 +1,37 @@
import React, { useContext } from 'react'
import { User } from '../../context'
import styles from './ActionResponse.module.scss'
export const ActionSuccess = ({
success,
trxHash
}: {
success: string
trxHash: string
}) => {
const { network } = useContext(User)
const submarineLink = `https://submarine.${
network === 'pacific' ? 'oceanprotocol' : `${network}.dev-ocean`
}.com/tx/${trxHash}`
return (
<div className={styles.success}>
<strong>{success}</strong>
{trxHash && (
<p>
<strong>Your Transaction Hash</strong>
<a href={submarineLink}>
<code>{trxHash}</code>
</a>
</p>
)}
</div>
)
}
export const ActionError = ({ error }: { error: string }) => (
<div className={styles.error}>
<p>{error}</p>
</div>
)

View File

@ -0,0 +1,7 @@
@import '../../styles/variables';
.actions {
display: grid;
gap: $spacer / 2;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}

View File

@ -2,9 +2,9 @@ import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import { MemoryRouter } from 'react-router'
import { createMemoryHistory, createLocation } from 'history'
import Faucet from './Faucet'
import { User } from '../context'
import { userMockConnected } from '../../__mocks__/user-mock'
import Faucet from '.'
import { User } from '../../context'
import { userMockConnected } from '../../../__mocks__/user-mock'
const history = createMemoryHistory()
const location = createLocation('/faucet')
@ -21,7 +21,7 @@ const setup = () => {
</MemoryRouter>
</User.Provider>
)
const button = utils.getByText('Request Ether')
const button = utils.getByText('Request ETH')
const { container } = utils
return {
button,

View File

@ -0,0 +1,31 @@
import React, { useContext } from 'react'
import Route from '../../components/templates/Route'
import { Market } from '../../context'
import Web3message from '../../components/organisms/Web3message'
import Content from '../../components/atoms/Content'
import withTracker from '../../hoc/withTracker'
import { showRequestTokens } from '../../config'
import Action from './Action'
import styles from './index.module.scss'
function Faucet() {
const { network } = useContext(Market)
return (
<Route
title="Faucet"
description={`Shower yourself with some Ether for Ocean's ${network} network.`}
>
<Content>
<Web3message />
<div className={styles.actions}>
<Action token="ETH" />
{showRequestTokens && <Action token="OCEAN" />}
</div>
</Content>
</Route>
)
}
export default withTracker(Faucet)

View File

@ -3,28 +3,28 @@ context('Faucet', () => {
before(() => {
cy.visit('/faucet')
// Wait for end of loading
cy.get('button[name="FaucetEther"]', { timeout: 60000 }).should(
cy.get('button[name="FaucetETH"]', { timeout: 60000 }).should(
'have.length',
1
)
})
beforeEach(() => {
cy.get('button[name="FaucetEther"]')
cy.get('button[name="FaucetETH"]')
.first()
.as('button')
})
it('Faucet button is clickable when user is connected.', () => {
cy.get('@button')
.contains('Request Ether')
.contains('Request ETH')
.should('not.be.disabled')
})
it('Execute faucet call', () => {
// Execute call
cy.get('@button')
.contains('Request Ether')
.contains('Request ETH')
.click()
// Verify that we got response from server
cy.contains(/(Successfully added|Already requested)/, {