mirror of
https://github.com/oceanprotocol/commons.git
synced 2023-03-15 18:03:00 +01:00
Merge pull request #202 from oceanprotocol/fix/faucet
Faucet route refactor
This commit is contained in:
commit
2c99d6f9e6
@ -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)
|
12
client/src/routes/Faucet/Action.module.scss
Normal file
12
client/src/routes/Faucet/Action.module.scss
Normal 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;
|
||||
}
|
||||
}
|
98
client/src/routes/Faucet/Action.tsx
Normal file
98
client/src/routes/Faucet/Action.tsx
Normal 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>
|
||||
)
|
||||
}
|
@ -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;
|
37
client/src/routes/Faucet/ActionResponse.tsx
Normal file
37
client/src/routes/Faucet/ActionResponse.tsx
Normal 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>
|
||||
)
|
7
client/src/routes/Faucet/index.module.scss
Normal file
7
client/src/routes/Faucet/index.module.scss
Normal file
@ -0,0 +1,7 @@
|
||||
@import '../../styles/variables';
|
||||
|
||||
.actions {
|
||||
display: grid;
|
||||
gap: $spacer / 2;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
}
|
@ -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, Market } from '../../context'
|
||||
import { userMockConnected } from '../../../__mocks__/user-mock'
|
||||
|
||||
const history = createMemoryHistory()
|
||||
const location = createLocation('/faucet')
|
||||
@ -12,16 +12,25 @@ const location = createLocation('/faucet')
|
||||
const setup = () => {
|
||||
const utils = render(
|
||||
<User.Provider value={userMockConnected}>
|
||||
<MemoryRouter>
|
||||
<Faucet
|
||||
history={history}
|
||||
location={location}
|
||||
match={{ params: '', path: '', url: '', isExact: true }}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
<Market.Provider
|
||||
value={{
|
||||
network: 'pacific',
|
||||
totalAssets: 100,
|
||||
categories: [''],
|
||||
networkMatch: true
|
||||
}}
|
||||
>
|
||||
<MemoryRouter>
|
||||
<Faucet
|
||||
history={history}
|
||||
location={location}
|
||||
match={{ params: '', path: '', url: '', isExact: true }}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</Market.Provider>
|
||||
</User.Provider>
|
||||
)
|
||||
const button = utils.getByText('Request Ether')
|
||||
const button = utils.getByText('Request ETH')
|
||||
const { container } = utils
|
||||
return {
|
||||
button,
|
||||
@ -49,6 +58,6 @@ describe('Faucet', () => {
|
||||
fireEvent.click(button)
|
||||
expect(userMockConnected.requestFromFaucet).toHaveBeenCalledTimes(1)
|
||||
// check for spinner
|
||||
expect(getByText('Getting Ether...')).toBeInTheDocument()
|
||||
expect(getByText('Getting ETH...')).toBeInTheDocument()
|
||||
})
|
||||
})
|
33
client/src/routes/Faucet/index.tsx
Normal file
33
client/src/routes/Faucet/index.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
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 ETH ${
|
||||
showRequestTokens ? 'or OCEAN' : ''
|
||||
} 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)
|
@ -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)/, {
|
||||
|
Loading…
Reference in New Issue
Block a user