Skip to content

Commit

Permalink
feat: changing API urls does not need the app refresh (#173)
Browse files Browse the repository at this point in the history
* feat: changing API urls does not need the app refresh

* fix: propagate beeDebugApi and beeApi change to the refresh interval

* fix: any failed request on the Bee provider does not stop the execution of other requests

* fix: error handling for incorrect bee and bee debug urls

* fix: change debug API in the settings tab
  • Loading branch information
vojtechsimetka committed Aug 20, 2021
1 parent 2624cf0 commit d6d03bf
Show file tree
Hide file tree
Showing 21 changed files with 339 additions and 383 deletions.
31 changes: 17 additions & 14 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { lightTheme, darkTheme } from './theme'
import { Provider as StampsProvider } from './providers/Stamps'
import { Provider as PlatformProvider } from './providers/Platform'
import { Provider as BeeProvider } from './providers/Bee'
import { Provider as SettingsProvider } from './providers/Settings'

const App = (): ReactElement => {
const [themeMode, toggleThemeMode] = useState('light')
Expand All @@ -37,20 +38,22 @@ const App = (): ReactElement => {
return (
<div className="App">
<ThemeProvider theme={themeMode === 'light' ? lightTheme : darkTheme}>
<BeeProvider>
<StampsProvider>
<PlatformProvider>
<SnackbarProvider>
<>
<CssBaseline />
<Router>
<BaseRouter />
</Router>
</>
</SnackbarProvider>
</PlatformProvider>
</StampsProvider>
</BeeProvider>
<SettingsProvider>
<BeeProvider>
<StampsProvider>
<PlatformProvider>
<SnackbarProvider>
<>
<CssBaseline />
<Router>
<BaseRouter />
</Router>
</>
</SnackbarProvider>
</PlatformProvider>
</StampsProvider>
</BeeProvider>
</SettingsProvider>
</ThemeProvider>
</div>
)
Expand Down
11 changes: 7 additions & 4 deletions src/components/CashoutModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import DialogContent from '@material-ui/core/DialogContent'
import DialogContentText from '@material-ui/core/DialogContentText'
import DialogTitle from '@material-ui/core/DialogTitle'
import { useSnackbar } from 'notistack'
import { ReactElement, useState } from 'react'
import { beeDebugApi } from '../services/bee'
import { ReactElement, useState, useContext } from 'react'
import { Context as SettingsContext } from '../providers/Settings'
import EthereumAddress from './EthereumAddress'

interface Props {
Expand All @@ -19,6 +19,7 @@ export default function CheckoutModal({ peerId, uncashedAmount }: Props): ReactE
const [open, setOpen] = useState<boolean>(false)
const [loadingCashout, setLoadingCashout] = useState<boolean>(false)
const { enqueueSnackbar } = useSnackbar()
const { beeDebugApi } = useContext(SettingsContext)

const handleClickOpen = () => {
setOpen(true)
Expand All @@ -29,10 +30,12 @@ export default function CheckoutModal({ peerId, uncashedAmount }: Props): ReactE
}

const handleCashout = () => {
if (!beeDebugApi) return

if (peerId) {
setLoadingCashout(true)
beeDebugApi.chequebook
.peerCashout(peerId)
beeDebugApi
.cashoutLastCheque(peerId)
.then(res => {
setOpen(false)
enqueueSnackbar(
Expand Down
68 changes: 28 additions & 40 deletions src/components/ConnectToHost.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,50 @@
import React, { ReactElement, useState } from 'react'
import { TextField, Button, CircularProgress, Container } from '@material-ui/core'
import { TextField, Button } from '@material-ui/core'

interface Props {
defaultHost?: string
hostName: string
setHost: (host: string) => void
}

export default function ConnectToHost(props: Props): ReactElement {
const [hostInputVisible, toggleHostInputVisibility] = useState(false)
const [connectingToHost, setConnectingToHost] = useState(false)
const [host, setHost] = useState('')

const handleNewHostConnection = () => {
if (host) {
setConnectingToHost(true)
sessionStorage.setItem(props.hostName, host)
props.setHost(host)
toggleHostInputVisibility(!hostInputVisible)
window.location.reload()
}
}

return (
<div>
{
// FIXME: this should be broken up
/* eslint-disable no-nested-ternary */
hostInputVisible ? (
<div style={{ display: 'flex' }}>
<TextField
defaultValue={props.defaultHost}
label="Enter host"
variant="outlined"
size="small"
onChange={e => setHost(e.target.value)}
style={{ marginRight: '15px', minWidth: '300px' }}
/>
<Button onClick={() => handleNewHostConnection()} size="small" variant="outlined">
Connect
</Button>
<Button
style={{ marginLeft: '7px' }}
onClick={() => toggleHostInputVisibility(!hostInputVisible)}
size="small"
>
Cancel
</Button>
</div>
) : connectingToHost ? (
<Container style={{ textAlign: 'center', padding: '0px' }}>
<CircularProgress size={20} />
</Container>
) : (
<Button onClick={() => toggleHostInputVisibility(!hostInputVisible)} size="small" variant="outlined">
Change host
{hostInputVisible ? (
<div style={{ display: 'flex' }}>
<TextField
defaultValue={props.defaultHost}
label="Enter host"
variant="outlined"
size="small"
onChange={e => setHost(e.target.value)}
style={{ marginRight: '15px', minWidth: '300px' }}
/>
<Button onClick={() => handleNewHostConnection()} size="small" variant="outlined">
Connect
</Button>
)
/* eslint-enable no-nested-ternary */
}
<Button
style={{ marginLeft: '7px' }}
onClick={() => toggleHostInputVisibility(!hostInputVisible)}
size="small"
>
Cancel
</Button>
</div>
) : (
<Button onClick={() => toggleHostInputVisibility(!hostInputVisible)} size="small" variant="outlined">
Change host
</Button>
)}
</div>
)
}
4 changes: 0 additions & 4 deletions src/constants.ts

This file was deleted.

12 changes: 9 additions & 3 deletions src/containers/DepositModal.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import type { ReactElement } from 'react'
import { beeDebugApi } from '../services/bee'
import { ReactElement, useContext } from 'react'
import { Context as SettingsContext } from '../providers/Settings'

import WDModal from '../components/WDModal'
import { BigNumber } from 'bignumber.js'

export default function DepositModal(): ReactElement {
const { beeDebugApi } = useContext(SettingsContext)

return (
<WDModal
successMessage="Successful deposit."
errorMessage="Error with depositing"
dialogMessage="Specify the amount of BZZ you would like to withdraw from your node."
label="Deposit"
min={new BigNumber(0)}
action={beeDebugApi.chequebook.deposit}
action={(amount: bigint) => {
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')

return beeDebugApi.depositTokens(amount.toString())
}}
/>
)
}
12 changes: 9 additions & 3 deletions src/containers/WithdrawModal.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import type { ReactElement } from 'react'
import { beeDebugApi } from '../services/bee'
import { ReactElement, useContext } from 'react'
import { Context as SettingsContext } from '../providers/Settings'

import WDModal from '../components/WDModal'
import { BigNumber } from 'bignumber.js'

export default function WithdrawModal(): ReactElement {
const { beeDebugApi } = useContext(SettingsContext)

return (
<WDModal
successMessage="Successful withdrawl."
errorMessage="Error with withdrawing."
dialogMessage="Specify the amount of BZZ you would like to withdraw from your node."
label="Withdraw"
min={new BigNumber(0)}
action={beeDebugApi.chequebook.withdraw}
action={(amount: bigint) => {
if (!beeDebugApi) throw new Error('Bee Debug URL is not valid')

return beeDebugApi.withdrawTokens(amount.toString())
}}
/>
)
}
34 changes: 12 additions & 22 deletions src/hooks/accounting.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import { LastCashoutActionResponse } from '@ethersphere/bee-js'
import { LastCashoutActionResponse, BeeDebug } from '@ethersphere/bee-js'
import { useEffect, useState } from 'react'
import { Token } from '../models/Token'
import { beeDebugApi } from '../services/bee'
import { makeRetriablePromise, unwrapPromiseSettlements } from '../utils'
import { Balance, Settlement, useApiPeerBalances, useApiSettlements } from './apiHooks'
import { Balance, Settlements, Settlement } from '../types'

interface UseAccountingHook {
isLoading: boolean
isLoadingUncashed: boolean
error: Error | null
totalsent: Token
totalreceived: Token
accounting: Accounting[] | null
}

Expand Down Expand Up @@ -77,39 +72,34 @@ function mergeAccounting(
)
}

export const useAccounting = (): UseAccountingHook => {
const settlements = useApiSettlements()
const balances = useApiPeerBalances()

export const useAccounting = (
beeDebugApi: BeeDebug | null,
settlements: Settlements | null,
balances: Balance[] | null,
): UseAccountingHook => {
const [isLoadingUncashed, setIsloadingUncashed] = useState<boolean>(false)
const [uncashedAmounts, setUncashedAmounts] = useState<LastCashoutActionResponse[] | undefined>(undefined)

const error = balances.error || settlements.error

useEffect(() => {
// We don't have any settlements loaded yet or we are already loading/have loaded the uncashed amounts
if (isLoadingUncashed || !settlements.settlements || uncashedAmounts || error) return
if (isLoadingUncashed || !beeDebugApi || !settlements || uncashedAmounts) return

setIsloadingUncashed(true)
const promises = settlements.settlements.settlements
const promises = settlements.settlements
.filter(({ received }) => received.toBigNumber.gt('0'))
.map(({ peer }) => makeRetriablePromise(() => beeDebugApi.chequebook.getPeerLastCashout(peer)))
.map(({ peer }) => makeRetriablePromise(() => beeDebugApi.getLastCashoutAction(peer)))

Promise.allSettled(promises).then(settlements => {
const results = unwrapPromiseSettlements(settlements)
setUncashedAmounts(results.fulfilled)
setIsloadingUncashed(false)
})
}, [settlements, isLoadingUncashed, uncashedAmounts, error])
}, [settlements, isLoadingUncashed, uncashedAmounts])

const accounting = mergeAccounting(balances.peerBalances, settlements.settlements?.settlements, uncashedAmounts)
const accounting = mergeAccounting(balances, settlements?.settlements, uncashedAmounts)

return {
isLoading: settlements.isLoadingSettlements || balances.isLoadingPeerBalances,
isLoadingUncashed,
error,
accounting,
totalsent: settlements.settlements?.totalSent || new Token('0'),
totalreceived: settlements.settlements?.totalReceived || new Token('0'),
}
}
89 changes: 0 additions & 89 deletions src/hooks/apiHooks.tsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,5 @@
import { useState, useEffect } from 'react'

import { beeDebugApi } from '../services/bee'
import axios from 'axios'
import { Token } from '../models/Token'

export interface Balance {
peer: string
balance: Token
}

export interface PeerBalanceHook {
peerBalances: Balance[] | null
isLoadingPeerBalances: boolean
error: Error | null
}

export const useApiPeerBalances = (): PeerBalanceHook => {
const [peerBalances, setPeerBalances] = useState<Balance[] | null>(null)
const [isLoadingPeerBalances, setLoading] = useState<boolean>(true)
const [error, setError] = useState<Error | null>(null)

useEffect(() => {
setLoading(true)
beeDebugApi.balance
.balances()
.then(res => {
// for some reason sometimes these are numbers and not BigInts
const balances = res.balances.map(({ peer, balance }) => ({ peer, balance: new Token(balance) }))
setPeerBalances(balances)
})
.catch(error => {
setError(error)
})
.finally(() => {
setLoading(false)
})
}, [])

return { peerBalances, isLoadingPeerBalances, error }
}

export interface Settlement {
peer: string
received: Token
sent: Token
}

export interface Settlements {
totalReceived: Token
totalSent: Token
settlements: Settlement[]
}

export interface SettlementsHook {
settlements: Settlements | null
isLoadingSettlements: boolean
error: Error | null
}

export const useApiSettlements = (): SettlementsHook => {
const [settlements, setSettlements] = useState<Settlements | null>(null)
const [isLoadingSettlements, setLoading] = useState<boolean>(true)
const [error, setError] = useState<Error | null>(null)

useEffect(() => {
setLoading(true)
beeDebugApi.settlements
.getSettlements()
.then(({ totalReceived, settlements, totalSent }) => {
const set = {
totalReceived: new Token(totalReceived),
totalSent: new Token(totalSent),
settlements: settlements.map(({ peer, received, sent }) => ({
peer,
received: new Token(received),
sent: new Token(sent),
})),
}
setSettlements(set)
})
.catch(error => {
setError(error)
})
.finally(() => {
setLoading(false)
})
}, [])

return { settlements, isLoadingSettlements, error }
}

export interface LatestBeeReleaseHook {
latestBeeRelease: LatestBeeRelease | null
Expand Down
Loading

0 comments on commit d6d03bf

Please sign in to comment.