From 95d796bda28575df252eb4fd6e1c9b04327b06a6 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Wed, 24 Jan 2024 17:21:23 -0500 Subject: [PATCH 1/4] refactor USDC balance recovery logic --- packages/common/src/hooks/useUSDCBalance.ts | 11 ++++- packages/common/src/store/buy-usdc/sagas.ts | 28 +++++++++++ .../hooks/usePurchaseContentFormState.ts | 9 +--- .../usdc-balance-pill/USDCBalancePill.tsx | 7 +-- .../hooks/usePurchaseContentFormState.ts | 11 +---- .../usdc-balance-pill/USDCBalancePill.tsx | 5 +- .../components/ErrorPage.tsx | 2 + .../components/TransferSuccessful.tsx | 2 +- .../pages/dashboard-page/DashboardPage.tsx | 20 +------- .../pay-and-earn-page/PayAndEarnPage.tsx | 10 +--- .../pay-and-earn-page/components/USDCCard.tsx | 48 +++++++++++-------- .../components/WithdrawalsTab.tsx | 1 + .../desktop/PayAndEarnPage.tsx | 13 ++--- .../mobile/PayAndEarnPage.tsx | 13 ++--- 14 files changed, 86 insertions(+), 94 deletions(-) diff --git a/packages/common/src/hooks/useUSDCBalance.ts b/packages/common/src/hooks/useUSDCBalance.ts index a58c5bc0afb..1d58c38b995 100644 --- a/packages/common/src/hooks/useUSDCBalance.ts +++ b/packages/common/src/hooks/useUSDCBalance.ts @@ -55,6 +55,7 @@ export const useUSDCBalance = ({ } }, [audiusBackend, setData]) + // Refresh balance on mount useEffect(() => { refresh() }, [refresh]) @@ -69,5 +70,13 @@ export const useUSDCBalance = ({ clearInterval(id) }, [id]) - return { balanceStatus, recoveryStatus, data, refresh, cancelPolling } + // If we haven't loaded the balance yet for the first time or we're + // actively recovering, then we will be in loading state. + const status = + balanceStatus === Status.IDLE || + (balanceStatus === Status.LOADING && data === null) || + recoveryStatus === Status.LOADING + ? Status.LOADING + : balanceStatus + return { status, data, refresh, cancelPolling } } diff --git a/packages/common/src/store/buy-usdc/sagas.ts b/packages/common/src/store/buy-usdc/sagas.ts index 3ba790fc3d0..8218bae91ce 100644 --- a/packages/common/src/store/buy-usdc/sagas.ts +++ b/packages/common/src/store/buy-usdc/sagas.ts @@ -46,6 +46,8 @@ import { } from './slice' import { BuyUSDCError, BuyUSDCErrorCode } from './types' import { getBuyUSDCRemoteConfig, getUSDCUserBank } from './utils' +import { setUSDCBalance } from 'store/wallet/slice' +import { StringUSDC } from 'models/Wallet' type PurchaseStepParams = { desiredAmount: number @@ -424,6 +426,17 @@ function* recoverPurchaseIfNecessary() { return } + const userBankAccountInfo = yield* call( + getTokenAccountInfo, + audiusBackendInstance, + { + tokenAccount: userBank, + mint: 'usdc' + } + ) + + const userBankInitialBalance = userBankAccountInfo?.amount ?? BigInt(0) + const userBankAddress = userBank.toBase58() // Transfer all USDC from the from the root wallet to the user bank @@ -480,6 +493,21 @@ function* recoverPurchaseIfNecessary() { } ) + const updatedBalance = yield* call( + pollForTokenBalanceChange, + audiusBackendInstance, + { + tokenAccount: userBank, + mint: 'usdc', + initialBalance: userBankInitialBalance, + maxRetryCount: TRANSACTION_RETRY_COUNT + } + ) + + yield* put( + setUSDCBalance({ amount: updatedBalance.toString() as StringUSDC }) + ) + yield* put(recoveryStatusChanged({ status: Status.SUCCESS })) yield* call( track, diff --git a/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts b/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts index 362db120c31..09a03b37068 100644 --- a/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts +++ b/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts @@ -22,14 +22,7 @@ export const usePurchaseContentFormState = ({ price }: { price: number }) => { const error = useSelector(getPurchaseContentError) const isUnlocking = !error && isContentPurchaseInProgress(stage) - const { data: currentBalance, recoveryStatus, refresh } = useUSDCBalance() - - // Refresh balance on successful recovery - useEffect(() => { - if (recoveryStatus === Status.SUCCESS) { - refresh() - } - }, [recoveryStatus, refresh]) + const { data: currentBalance } = useUSDCBalance() const purchaseSummaryValues = usePurchaseSummaryValues({ price, diff --git a/packages/mobile/src/components/usdc-balance-pill/USDCBalancePill.tsx b/packages/mobile/src/components/usdc-balance-pill/USDCBalancePill.tsx index c21d5c45bbc..c4b42228d60 100644 --- a/packages/mobile/src/components/usdc-balance-pill/USDCBalancePill.tsx +++ b/packages/mobile/src/components/usdc-balance-pill/USDCBalancePill.tsx @@ -33,10 +33,7 @@ const useStyles = makeStyles(({ spacing, palette }) => ({ export const USDCBalancePill = () => { const styles = useStyles() - const { data: usdcBalance, balanceStatus: usdcBalanceStatus } = - useUSDCBalance() - const isUsdcBalanceLoading = - usdcBalance === null || usdcBalanceStatus === Status.LOADING + const { data: usdcBalance, status: usdcBalanceStatus } = useUSDCBalance() const balanceCents = formatUSDCWeiToFloorCentsNumber( (usdcBalance ?? new BN(0)) as BNUSDC ) @@ -44,7 +41,7 @@ export const USDCBalancePill = () => { return ( - {isUsdcBalanceLoading ? ( + {usdcBalanceStatus === Status.LOADING ? ( { const error = useSelector(getPurchaseContentError) const isUnlocking = !error && isContentPurchaseInProgress(stage) - const { data: currentBalance, recoveryStatus, refresh } = useUSDCBalance() - - // Refresh balance on successful recovery - useEffect(() => { - if (recoveryStatus === Status.SUCCESS) { - refresh() - } - }, [recoveryStatus, refresh]) + const { data: currentBalance } = useUSDCBalance() const purchaseSummaryValues = usePurchaseSummaryValues({ price, diff --git a/packages/web/src/components/usdc-balance-pill/USDCBalancePill.tsx b/packages/web/src/components/usdc-balance-pill/USDCBalancePill.tsx index 0b7dfec7b83..c5e881615e6 100644 --- a/packages/web/src/components/usdc-balance-pill/USDCBalancePill.tsx +++ b/packages/web/src/components/usdc-balance-pill/USDCBalancePill.tsx @@ -19,8 +19,7 @@ type USDCPillProps = { } export const USDCBalancePill = ({ className }: USDCPillProps) => { - const { data: balance, balanceStatus: usdcBalanceStatus } = useUSDCBalance() - const isLoading = balance === null || usdcBalanceStatus === Status.LOADING + const { data: balance, status: usdcBalanceStatus } = useUSDCBalance() const balanceCents = formatUSDCWeiToFloorCentsNumber( (balance ?? new BN(0)) as BNUSDC ) @@ -29,7 +28,7 @@ export const USDCBalancePill = ({ className }: USDCPillProps) => { return (
- {isLoading ? ( + {usdcBalanceStatus === Status.LOADING ? ( ) : ( ${balanceFormatted} diff --git a/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx b/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx index 9a54da83f2e..d9e1148ea94 100644 --- a/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx +++ b/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx @@ -28,6 +28,8 @@ const messages = { tryAgain: 'Try Again?' } +// TODO: If we fail after moving to the root account, the useUSDC hook will trigger a refresh and +// show zero balance? export const ErrorPage = () => { const { setData } = useWithdrawUSDCModal() const { data: balance } = useUSDCBalance() diff --git a/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx b/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx index 46805a7bedf..cc18196e343 100644 --- a/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx +++ b/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx @@ -60,7 +60,7 @@ export const TransferSuccessful = ({ priorBalanceCents: number onClickDone: () => void }) => { - const { data: balance, balanceStatus } = useUSDCBalance() + const { data: balance, status: balanceStatus } = useUSDCBalance() const signature = useSelector(getWithdrawTransaction) const balanceNumber = formatUSDCWeiToFloorCentsNumber( (balance ?? new BN(0)) as BNUSDC diff --git a/packages/web/src/pages/dashboard-page/DashboardPage.tsx b/packages/web/src/pages/dashboard-page/DashboardPage.tsx index 8695c0882b0..7bad31e0941 100644 --- a/packages/web/src/pages/dashboard-page/DashboardPage.tsx +++ b/packages/web/src/pages/dashboard-page/DashboardPage.tsx @@ -1,13 +1,6 @@ import { useState, Suspense, ReactNode, useEffect, useCallback } from 'react' -import { - Status, - Track, - formatCount, - themeSelectors, - combineStatuses, - useUSDCBalance -} from '@audius/common' +import { Status, Track, formatCount, themeSelectors } from '@audius/common' import cn from 'classnames' import { each } from 'lodash' import moment, { Moment } from 'moment' @@ -77,12 +70,6 @@ export const DashboardPage = () => { const listenData = useSelector(getDashboardListenData) const dashboardStatus = useSelector(getDashboardStatus) const theme = useSelector(getTheme) - const { data: balance, balanceStatus } = useUSDCBalance() - const statuses = [dashboardStatus] - if (balance === null) { - statuses.push(balanceStatus) - } - const status = combineStatuses(statuses) const header =
@@ -187,10 +174,7 @@ export const DashboardPage = () => { contentClassName={styles.pageContainer} header={header} > - {!account || - balance === null || - !listenData || - status === Status.LOADING ? ( + {!account || !listenData || dashboardStatus === Status.LOADING ? ( ) : ( <> diff --git a/packages/web/src/pages/pay-and-earn-page/PayAndEarnPage.tsx b/packages/web/src/pages/pay-and-earn-page/PayAndEarnPage.tsx index c2805c9be34..5deab191da6 100644 --- a/packages/web/src/pages/pay-and-earn-page/PayAndEarnPage.tsx +++ b/packages/web/src/pages/pay-and-earn-page/PayAndEarnPage.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react' -import { Status, buyUSDCActions, useUSDCBalance } from '@audius/common' +import { buyUSDCActions } from '@audius/common' import { useDispatch } from 'react-redux' import { useIsMobile } from 'utils/clientUtil' @@ -12,14 +12,6 @@ import { PayAndEarnPageProps } from './types' export const PayAndEarnPage = (props: PayAndEarnPageProps) => { const isMobile = useIsMobile() const dispatch = useDispatch() - const { recoveryStatus, refresh } = useUSDCBalance() - - // Refresh balance on successful recovery - useEffect(() => { - if (recoveryStatus === Status.SUCCESS) { - refresh() - } - }, [recoveryStatus, refresh]) // Always check for recoverable USDC on page load useEffect(() => { diff --git a/packages/web/src/pages/pay-and-earn-page/components/USDCCard.tsx b/packages/web/src/pages/pay-and-earn-page/components/USDCCard.tsx index eef22f97f3b..70489398904 100644 --- a/packages/web/src/pages/pay-and-earn-page/components/USDCCard.tsx +++ b/packages/web/src/pages/pay-and-earn-page/components/USDCCard.tsx @@ -7,7 +7,9 @@ import { formatCurrencyBalance, formatUSDCWeiToFloorCentsNumber, useWithdrawUSDCModal, - useAddFundsModal + useAddFundsModal, + useUSDCBalance, + Status } from '@audius/common' import { Button, PlainButton, IconQuestionCircle, Flex } from '@audius/harmony' import { LogoUSDC } from '@audius/stems' @@ -34,15 +36,10 @@ const messages = { withdrawalHistory: 'Withdrawal History' } -export const USDCCard = ({ - balance, - refreshing = false -}: { - balance: BNUSDC - refreshing?: boolean -}) => { +export const USDCCard = () => { const { onOpen: openWithdrawUSDCModal } = useWithdrawUSDCModal() const { onOpen: openAddFundsModal } = useAddFundsModal() + const { data: balance, status: balanceStatus } = useUSDCBalance() const balanceCents = formatUSDCWeiToFloorCentsNumber( (balance ?? new BN(0)) as BNUSDC @@ -93,15 +90,18 @@ export const USDCCard = ({
- {refreshing && } - - ${balanceFormatted} - + {balanceStatus === Status.LOADING ? ( + + ) : ( + + ${balanceFormatted} + + )}
@@ -117,12 +117,22 @@ export const USDCCard = ({
-
-
diff --git a/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx b/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx index e5aa4af5543..c5a3fa1d2e7 100644 --- a/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx +++ b/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx @@ -212,6 +212,7 @@ export const WithdrawalsTab = ({ ? (['date', 'amount'] as WithdrawalsTableColumn[]) : undefined + // TODO: Loading spinner while USDC balance is loading, probably has to be done at parent component return (
{isEmpty ? ( diff --git a/packages/web/src/pages/pay-and-earn-page/desktop/PayAndEarnPage.tsx b/packages/web/src/pages/pay-and-earn-page/desktop/PayAndEarnPage.tsx index 10518f28355..267f535ef05 100644 --- a/packages/web/src/pages/pay-and-earn-page/desktop/PayAndEarnPage.tsx +++ b/packages/web/src/pages/pay-and-earn-page/desktop/PayAndEarnPage.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from 'react' -import { useUSDCBalance, accountSelectors, Status } from '@audius/common' +import { accountSelectors } from '@audius/common' import { Button, Flex, @@ -42,7 +42,6 @@ type TableMetadata = { export const PayAndEarnPage = ({ tableView }: PayAndEarnPageProps) => { const dispatch = useDispatch() - const { data: balance, balanceStatus, recoveryStatus } = useUSDCBalance() const accountHasTracks = useSelector(getAccountHasTracks) const [tableOptions, setTableOptions] = useState(null) @@ -135,17 +134,11 @@ export const PayAndEarnPage = ({ tableView }: PayAndEarnPageProps) => { contentClassName={styles.pageContainer} header={header} > - {!tableOptions || !selectedTable || balance === null ? ( + {!tableOptions || !selectedTable ? ( ) : ( <> - + { const dispatch = useDispatch() - const { data: balance, balanceStatus, recoveryStatus } = useUSDCBalance() const accountHasTracks = useSelector(getAccountHasTracks) const [tableOptions, setTableOptions] = useState(null) @@ -134,17 +133,11 @@ export const PayAndEarnPage = ({ tableView }: PayAndEarnPageProps) => { description={messages.description} containerClassName={styles.mobilePageContainer} > - {!tableOptions || !selectedTable || balance === null ? ( + {!tableOptions || !selectedTable ? ( ) : ( <> - + Date: Wed, 24 Jan 2024 18:59:41 -0500 Subject: [PATCH 2/4] Make spinner larger --- .../pages/pay-and-earn-page/components/USDCCard.module.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/src/pages/pay-and-earn-page/components/USDCCard.module.css b/packages/web/src/pages/pay-and-earn-page/components/USDCCard.module.css index 39fab796fb2..93fdf499c2e 100644 --- a/packages/web/src/pages/pay-and-earn-page/components/USDCCard.module.css +++ b/packages/web/src/pages/pay-and-earn-page/components/USDCCard.module.css @@ -48,8 +48,8 @@ } .spinner { - height: var(--unit-5); - width: var(--unit-5); + height: var(--unit-8); + width: var(--unit-8); align-self: center; } From a49aeada59e98e2bfa515f691ab13b61e6bc6981 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:09:44 -0500 Subject: [PATCH 3/4] cleanup --- .../src/components/withdraw-usdc-modal/components/ErrorPage.tsx | 2 -- .../src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx | 1 - 2 files changed, 3 deletions(-) diff --git a/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx b/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx index d9e1148ea94..9a54da83f2e 100644 --- a/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx +++ b/packages/web/src/components/withdraw-usdc-modal/components/ErrorPage.tsx @@ -28,8 +28,6 @@ const messages = { tryAgain: 'Try Again?' } -// TODO: If we fail after moving to the root account, the useUSDC hook will trigger a refresh and -// show zero balance? export const ErrorPage = () => { const { setData } = useWithdrawUSDCModal() const { data: balance } = useUSDCBalance() diff --git a/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx b/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx index c5a3fa1d2e7..e5aa4af5543 100644 --- a/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx +++ b/packages/web/src/pages/pay-and-earn-page/components/WithdrawalsTab.tsx @@ -212,7 +212,6 @@ export const WithdrawalsTab = ({ ? (['date', 'amount'] as WithdrawalsTableColumn[]) : undefined - // TODO: Loading spinner while USDC balance is loading, probably has to be done at parent component return (
{isEmpty ? ( From dea47b26637d73c34c118868d41a22482efbcb80 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:24:13 -0500 Subject: [PATCH 4/4] fix lint errors --- .../hooks/usePurchaseContentFormState.ts | 7 ++----- .../hooks/usePurchaseContentFormState.ts | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts b/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts index 09a03b37068..90f2bc1ed40 100644 --- a/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts +++ b/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseContentFormState.ts @@ -1,10 +1,7 @@ -import { useEffect } from 'react' - import { - purchaseContentSelectors, isContentPurchaseInProgress, - useUSDCBalance, - Status + purchaseContentSelectors, + useUSDCBalance } from '@audius/common' import { useSelector } from 'react-redux' diff --git a/packages/web/src/components/premium-content-purchase-modal/hooks/usePurchaseContentFormState.ts b/packages/web/src/components/premium-content-purchase-modal/hooks/usePurchaseContentFormState.ts index ae9c495b885..2e142df0c09 100644 --- a/packages/web/src/components/premium-content-purchase-modal/hooks/usePurchaseContentFormState.ts +++ b/packages/web/src/components/premium-content-purchase-modal/hooks/usePurchaseContentFormState.ts @@ -1,8 +1,7 @@ import { purchaseContentSelectors, isContentPurchaseInProgress, - useUSDCBalance, - Status + useUSDCBalance } from '@audius/common' import { useSelector } from 'react-redux'