From 7ba0e74baa92a2cc2e9f6d4067e1d6db194b0bd2 Mon Sep 17 00:00:00 2001 From: Vojtech Simetka Date: Wed, 3 Aug 2022 13:54:54 +0200 Subject: [PATCH] refactor: extract the wallet balance into a provider --- src/App.tsx | 39 +++++----- src/pages/account/wallet/AccountWallet.tsx | 4 +- src/pages/gift-code/index.tsx | 4 +- src/pages/info/index.tsx | 22 ++++-- src/pages/top-up/Balance.tsx | 4 +- src/pages/top-up/GiftCardFund.tsx | 7 +- src/pages/top-up/Swap.tsx | 7 +- src/pages/top-up/index.tsx | 5 +- src/providers/Bee.tsx | 32 ++------ src/providers/WalletBalance.tsx | 88 ++++++++++++++++++++++ 10 files changed, 150 insertions(+), 62 deletions(-) create mode 100644 src/providers/WalletBalance.tsx diff --git a/src/App.tsx b/src/App.tsx index 5bca4df9..5b224401 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,6 +13,7 @@ import { Provider as PlatformProvider } from './providers/Platform' import { Provider as SettingsProvider } from './providers/Settings' import { Provider as StampsProvider } from './providers/Stamps' import { Provider as TopUpProvider } from './providers/TopUp' +import { Provider as BalanceProvider } from './providers/WalletBalance' import BaseRouter from './routes' import { theme } from './theme' import { config } from './config' @@ -43,24 +44,26 @@ const App = ({ beeApiUrl, beeDebugApiUrl, lockedApiSettings, isBeeDesktop }: Pro > - - - - - - - <> - - - - - - - - - - - + + + + + + + + <> + + + + + + + + + + + + diff --git a/src/pages/account/wallet/AccountWallet.tsx b/src/pages/account/wallet/AccountWallet.tsx index 6173b2b7..a4a859ac 100644 --- a/src/pages/account/wallet/AccountWallet.tsx +++ b/src/pages/account/wallet/AccountWallet.tsx @@ -13,13 +13,15 @@ import { SwarmButton } from '../../../components/SwarmButton' import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard' import { CheckState, Context as BeeContext } from '../../../providers/Bee' import { Context as SettingsContext } from '../../../providers/Settings' +import { Context as BalanceProvider } from '../../../providers/WalletBalance' import { ROUTES } from '../../../routes' import { AccountNavigation } from '../AccountNavigation' import { Header } from '../Header' export function AccountWallet(): ReactElement { - const { balance, nodeAddresses, nodeInfo, status } = useContext(BeeContext) + const { nodeAddresses, nodeInfo, status } = useContext(BeeContext) const { isBeeDesktop } = useContext(SettingsContext) + const { balance } = useContext(BalanceProvider) const navigate = useNavigate() diff --git a/src/pages/gift-code/index.tsx b/src/pages/gift-code/index.tsx index ca42e13a..12f5a4bb 100644 --- a/src/pages/gift-code/index.tsx +++ b/src/pages/gift-code/index.tsx @@ -11,7 +11,7 @@ import ExpandableListItemKey from '../../components/ExpandableListItemKey' import { HistoryHeader } from '../../components/HistoryHeader' import { Loading } from '../../components/Loading' import { SwarmButton } from '../../components/SwarmButton' -import { Context as BeeContext } from '../../providers/Bee' +import { Context as BalanceProvider } from '../../providers/WalletBalance' import { Context as TopUpContext } from '../../providers/TopUp' import { Context as SettingsContext } from '../../providers/Settings' import { createGiftWallet } from '../../utils/desktop' @@ -24,7 +24,7 @@ const GIFT_WALLET_FUND_BZZ_AMOUNT = Token.fromDecimal('0.5', 16) export default function Index(): ReactElement { const { giftWallets, addGiftWallet } = useContext(TopUpContext) const { provider } = useContext(SettingsContext) - const { balance } = useContext(BeeContext) + const { balance } = useContext(BalanceProvider) const [loading, setLoading] = useState(false) const [balances, setBalances] = useState([]) diff --git a/src/pages/info/index.tsx b/src/pages/info/index.tsx index 9917919f..3c3f01f1 100644 --- a/src/pages/info/index.tsx +++ b/src/pages/info/index.tsx @@ -6,6 +6,7 @@ import Upload from 'remixicon-react/UploadLineIcon' import { Context as BeeContext } from '../../providers/Bee' import { Context as SettingsContext } from '../../providers/Settings' +import { Context as BalanceProvider } from '../../providers/WalletBalance' import Card from '../../components/Card' import Map from '../../components/Map' import ExpandableListItem from '../../components/ExpandableListItem' @@ -24,15 +25,24 @@ export default function Status(): ReactElement { latestBeeVersionUrl, topology, nodeInfo, - balance, chequebookBalance, - wallet, + chainId, } = useContext(BeeContext) const { isBeeDesktop } = useContext(SettingsContext) + const { balance, error } = useContext(BalanceProvider) const { beeDesktopVersion } = useIsBeeDesktop() const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isBeeDesktop) const navigate = useNavigate() + let balanceText = 'Loading...' + + if (error) { + balanceText = 'Could not load...' + console.error(error) // eslint-disable-line + } else if (balance) { + balanceText = `${balance.bzz.toSignificantDigits(4)} xBZZ | ${balance.dai.toSignificantDigits(4)} xDAI` + } + return (
@@ -46,11 +56,7 @@ export default function Status(): ReactElement { onClick: () => navigate(ROUTES.ACCOUNT_WALLET), }} icon={} - title={ - balance - ? `${balance?.bzz.toSignificantDigits(4)} xBZZ | ${balance?.dai.toSignificantDigits(4)} xDAI` - : 'Loading...' - } + title={balanceText} subtitle="Current wallet balance." status="ok" /> @@ -153,7 +159,7 @@ export default function Status(): ReactElement { } /> - {wallet && } + {chainId && }
) } diff --git a/src/pages/top-up/Balance.tsx b/src/pages/top-up/Balance.tsx index 2f3c71f4..084643f2 100644 --- a/src/pages/top-up/Balance.tsx +++ b/src/pages/top-up/Balance.tsx @@ -9,6 +9,7 @@ import { Loading } from '../../components/Loading' import { SwarmButton } from '../../components/SwarmButton' import { SwarmDivider } from '../../components/SwarmDivider' import { Context } from '../../providers/Bee' +import { Context as BalanceProvider } from '../../providers/WalletBalance' import { TopUpProgressIndicator } from './TopUpProgressIndicator' const MINIMUM_XDAI = '0.5' @@ -21,7 +22,8 @@ interface Props { } export default function Index({ header, title, p, next }: Props): ReactElement { - const { nodeAddresses, balance } = useContext(Context) + const { nodeAddresses } = useContext(Context) + const { balance } = useContext(BalanceProvider) const navigate = useNavigate() if (!balance || !nodeAddresses) { diff --git a/src/pages/top-up/GiftCardFund.tsx b/src/pages/top-up/GiftCardFund.tsx index 6f2f23f3..58ec8d0a 100644 --- a/src/pages/top-up/GiftCardFund.tsx +++ b/src/pages/top-up/GiftCardFund.tsx @@ -13,6 +13,7 @@ import { SwarmButton } from '../../components/SwarmButton' import { SwarmDivider } from '../../components/SwarmDivider' import { Context as BeeContext } from '../../providers/Bee' import { Context as SettingsContext } from '../../providers/Settings' +import { Context as BalanceProvider } from '../../providers/WalletBalance' import { ROUTES } from '../../routes' import { sleepMs } from '../../utils' import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop' @@ -20,9 +21,9 @@ import { ResolvedWallet } from '../../utils/wallet' import { BeeModes } from '@ethersphere/bee-js' export function GiftCardFund(): ReactElement { - const { isBeeDesktop } = useContext(SettingsContext) - const { nodeAddresses, balance, nodeInfo } = useContext(BeeContext) - const { provider, providerUrl } = useContext(SettingsContext) + const { nodeAddresses, nodeInfo } = useContext(BeeContext) + const { isBeeDesktop, provider, providerUrl } = useContext(SettingsContext) + const { balance } = useContext(BalanceProvider) const [loading, setLoading] = useState(false) const [wallet, setWallet] = useState(null) diff --git a/src/pages/top-up/Swap.tsx b/src/pages/top-up/Swap.tsx index 7134cf47..246ae173 100644 --- a/src/pages/top-up/Swap.tsx +++ b/src/pages/top-up/Swap.tsx @@ -16,6 +16,7 @@ import { SwarmDivider } from '../../components/SwarmDivider' import { SwarmTextInput } from '../../components/SwarmTextInput' import { BzzToken } from '../../models/BzzToken' import { DaiToken } from '../../models/DaiToken' +import { Context as BalanceProvider } from '../../providers/WalletBalance' import { Context as BeeContext } from '../../providers/Bee' import { Context as SettingsContext } from '../../providers/Settings' import { ROUTES } from '../../routes' @@ -36,9 +37,9 @@ export function Swap({ header }: Props): ReactElement { const [userInputSwap, setUserInputSwap] = useState(null) const [price, setPrice] = useState(DaiToken.fromDecimal('0.6', 18)) - const { providerUrl } = useContext(SettingsContext) - const { balance, nodeAddresses, nodeInfo } = useContext(BeeContext) - const { isBeeDesktop } = useContext(SettingsContext) + const { providerUrl, isBeeDesktop } = useContext(SettingsContext) + const { nodeAddresses, nodeInfo } = useContext(BeeContext) + const { balance } = useContext(BalanceProvider) const navigate = useNavigate() const { enqueueSnackbar } = useSnackbar() diff --git a/src/pages/top-up/index.tsx b/src/pages/top-up/index.tsx index 35332968..b7a7bd3d 100644 --- a/src/pages/top-up/index.tsx +++ b/src/pages/top-up/index.tsx @@ -12,6 +12,8 @@ import { SwarmButton } from '../../components/SwarmButton' import { ROUTES } from '../../routes' import { CheckState, Context as BeeContext } from '../../providers/Bee' import { Context as SettingsContext } from '../../providers/Settings' +import { Context as BalanceProvider } from '../../providers/WalletBalance' +import config from '../../config' import { BeeModes } from '@ethersphere/bee-js' import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop' import { Loading } from '../../components/Loading' @@ -39,7 +41,8 @@ export default function TopUp(): ReactElement { const navigate = useNavigate() const styles = useStyles() const { isBeeDesktop } = useContext(SettingsContext) - const { balance, nodeInfo, status } = useContext(BeeContext) + const { nodeInfo, status } = useContext(BeeContext) + const { balance } = useContext(BalanceProvider) const { providerUrl } = useContext(SettingsContext) const [loading, setLoading] = useState(false) const { enqueueSnackbar } = useSnackbar() diff --git a/src/providers/Bee.tsx b/src/providers/Bee.tsx index b76f222a..1c86f661 100644 --- a/src/providers/Bee.tsx +++ b/src/providers/Bee.tsx @@ -8,7 +8,6 @@ import { NodeInfo, Peer, Topology, - WalletBalance, } from '@ethersphere/bee-js' import { createContext, ReactChild, ReactElement, useContext, useEffect, useState } from 'react' import semver from 'semver' @@ -16,7 +15,6 @@ import PackageJson from '../../package.json' import { useLatestBeeRelease } from '../hooks/apiHooks' import { Token } from '../models/Token' import type { Balance, ChequebookBalance, Settlements } from '../types' -import { WalletAddress } from '../utils/wallet' import { Context as SettingsContext } from './Settings' const REFRESH_WHEN_OK = 30_000 @@ -46,7 +44,6 @@ interface Status { interface ContextInterface { status: Status - balance: WalletAddress | null latestPublishedVersion?: string latestUserVersion?: string latestUserVersionExact?: string @@ -65,7 +62,7 @@ interface ContextInterface { peerCheques: LastChequesResponse | null settlements: Settlements | null chainState: ChainState | null - wallet: WalletBalance | null + chainId: number | null latestBeeRelease: LatestBeeRelease | null isLoading: boolean lastUpdate: number | null @@ -84,7 +81,6 @@ const initialValues: ContextInterface = { topology: { isEnabled: false, checkState: CheckState.ERROR }, chequebook: { isEnabled: false, checkState: CheckState.ERROR }, }, - balance: null, latestPublishedVersion: undefined, latestUserVersion: undefined, latestUserVersionExact: undefined, @@ -103,7 +99,7 @@ const initialValues: ContextInterface = { peerCheques: null, settlements: null, chainState: null, - wallet: null, + chainId: null, latestBeeRelease: null, isLoading: true, lastUpdate: null, @@ -191,7 +187,7 @@ function getStatus( let isRefreshing = false export function Provider({ children }: Props): ReactElement { - const { beeApi, beeDebugApi, provider } = useContext(SettingsContext) + const { beeApi, beeDebugApi } = useContext(SettingsContext) const [apiHealth, setApiHealth] = useState(false) const [debugApiHealth, setDebugApiHealth] = useState(null) const [nodeAddresses, setNodeAddresses] = useState(null) @@ -204,8 +200,7 @@ export function Provider({ children }: Props): ReactElement { const [peerCheques, setPeerCheques] = useState(null) const [settlements, setSettlements] = useState(null) const [chainState, setChainState] = useState(null) - const [walletAddress, setWalletAddress] = useState(initialValues.balance) - const [wallet, setWallet] = useState(null) + const [chainId, setChainId] = useState(null) const { latestBeeRelease } = useLatestBeeRelease() @@ -244,18 +239,6 @@ export function Provider({ children }: Props): ReactElement { if (beeDebugApi !== null) refresh() }, [beeDebugApi]) // eslint-disable-line react-hooks/exhaustive-deps - useEffect(() => { - if (nodeAddresses?.ethereum && provider) { - WalletAddress.make(nodeAddresses.ethereum, provider).then(setWalletAddress) - } - }, [nodeAddresses, provider]) - - useEffect(() => { - const interval = setInterval(() => walletAddress?.refresh().then(setWalletAddress), REFRESH_WHEN_OK) - - return () => clearInterval(interval) - }, [walletAddress]) - const refresh = async () => { // Don't want to refresh when already refreshing if (isRefreshing) return @@ -361,8 +344,8 @@ export function Provider({ children }: Props): ReactElement { // Wallet beeDebugApi .getWalletBalance({ timeout: TIMEOUT }) - .then(setWallet) - .catch(() => setWallet(null)), + .then(({ chainID }) => setChainId(chainID)) + .catch(() => setChainId(null)), // Chequebook balance chequeBalanceWrapper() @@ -429,7 +412,6 @@ export function Provider({ children }: Props): ReactElement { void + stop: () => void + refresh: () => Promise +} + +const initialValues: ContextInterface = { + balance: null, + error: null, + isLoading: false, + lastUpdate: null, + start: () => {}, // eslint-disable-line + stop: () => {}, // eslint-disable-line + refresh: () => Promise.reject(), +} + +export const Context = createContext(initialValues) +export const Consumer = Context.Consumer + +interface Props { + children: ReactChild +} + +export function Provider({ children }: Props): ReactElement { + const { provider } = useContext(SettingsContext) + const { nodeAddresses } = useContext(BeeContext) + const [balance, setBalance] = useState(initialValues.balance) + const [error, setError] = useState(initialValues.error) + const [isLoading, setIsLoading] = useState(initialValues.isLoading) + const [lastUpdate, setLastUpdate] = useState(initialValues.lastUpdate) + const [frequency, setFrequency] = useState(null) + + useEffect(() => { + if (nodeAddresses?.ethereum && provider) { + WalletAddress.make(nodeAddresses.ethereum, provider).then(setBalance) + } else { + setBalance(null) + } + }, [nodeAddresses, provider]) + + const refresh = async () => { + // Don't want to refresh when already refreshing + if (isLoading) return + + if (!balance) return + + try { + setIsLoading(true) + + setBalance(await balance.refresh()) + setLastUpdate(Date.now()) + } catch (e) { + setError(e as Error) + } finally { + setIsLoading(false) + } + } + + const start = (freq = 30000) => setFrequency(freq) + const stop = () => setFrequency(null) + + // Start the update loop + useEffect(() => { + refresh() + + // Start autorefresh only if the frequency is set + if (frequency) { + const interval = setInterval(refresh, frequency) + + return () => clearInterval(interval) + } + }, [frequency]) // eslint-disable-line react-hooks/exhaustive-deps + + return ( + + {children} + + ) +}