Skip to content

Commit

Permalink
[PAY-2238] Improve balance display with recovery process (#7322)
Browse files Browse the repository at this point in the history
  • Loading branch information
schottra authored and raymondjacobson committed Jan 25, 2024
1 parent 7a6976d commit cd72f9b
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 103 deletions.
11 changes: 10 additions & 1 deletion packages/common/src/hooks/useUSDCBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const useUSDCBalance = ({
}
}, [audiusBackend, setData])

// Refresh balance on mount
useEffect(() => {
refresh()
}, [refresh])
Expand All @@ -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 }
}
28 changes: 28 additions & 0 deletions packages/common/src/store/buy-usdc/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { useEffect } from 'react'

import {
purchaseContentSelectors,
isContentPurchaseInProgress,
useUSDCBalance,
Status
purchaseContentSelectors,
useUSDCBalance
} from '@audius/common'
import { useSelector } from 'react-redux'

Expand All @@ -22,14 +19,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,15 @@ 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
)
const usdcBalanceFormatted = formatCurrencyBalance(balanceCents / 100)
return (
<View style={styles.root}>
<LogoUSDC height={spacing(5)} width={spacing(5)} />
{isUsdcBalanceLoading ? (
{usdcBalanceStatus === Status.LOADING ? (
<Skeleton
style={styles.amount}
height={spacing(4.5)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { useEffect } from 'react'

import {
purchaseContentSelectors,
isContentPurchaseInProgress,
useUSDCBalance,
Status
useUSDCBalance
} from '@audius/common'
import { useSelector } from 'react-redux'

Expand All @@ -22,14 +19,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -29,7 +28,7 @@ export const USDCBalancePill = ({ className }: USDCPillProps) => {
return (
<div className={cn(styles.container, className)}>
<Icon className={styles.icon} icon={LogoUSDC} size='medium' />
{isLoading ? (
{usdcBalanceStatus === Status.LOADING ? (
<Skeleton className={styles.skeleton} />
) : (
<span className={styles.amount}>${balanceFormatted}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 2 additions & 18 deletions packages/web/src/pages/dashboard-page/DashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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 = <Header primary={messages.title} />

Expand Down Expand Up @@ -187,10 +174,7 @@ export const DashboardPage = () => {
contentClassName={styles.pageContainer}
header={header}
>
{!account ||
balance === null ||
!listenData ||
status === Status.LOADING ? (
{!account || !listenData || dashboardStatus === Status.LOADING ? (
<LoadingSpinner className={styles.spinner} />
) : (
<>
Expand Down
10 changes: 1 addition & 9 deletions packages/web/src/pages/pay-and-earn-page/PayAndEarnPage.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
}

.spinner {
height: var(--unit-5);
width: var(--unit-5);
height: var(--unit-8);
width: var(--unit-8);
align-self: center;
}

Expand Down
48 changes: 29 additions & 19 deletions packages/web/src/pages/pay-and-earn-page/components/USDCCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
Expand Down Expand Up @@ -93,15 +90,18 @@ export const USDCCard = ({
</div>

<Flex gap='m'>
{refreshing && <LoadingSpinner className={styles.spinner} />}
<Text
variant='heading'
color='staticWhite'
strength='strong'
size='xxLarge'
>
${balanceFormatted}
</Text>
{balanceStatus === Status.LOADING ? (
<LoadingSpinner className={styles.spinner} />
) : (
<Text
variant='heading'
color='staticWhite'
strength='strong'
size='xxLarge'
>
${balanceFormatted}
</Text>
)}
</Flex>
</div>
<div className={styles.usdcInfo}>
Expand All @@ -117,12 +117,22 @@ export const USDCCard = ({
</div>
<div className={styles.withdrawContainer}>
<div className={styles.addFundsButton}>
<Button variant='secondary' fullWidth onClick={handleAddFunds}>
<Button
variant='secondary'
fullWidth
onClick={handleAddFunds}
disabled={balanceStatus === Status.LOADING}
>
{messages.addFunds}
</Button>
</div>
<div className={styles.withdrawButton}>
<Button variant='secondary' fullWidth onClick={handleWithdraw}>
<Button
variant='secondary'
fullWidth
onClick={handleWithdraw}
disabled={balanceStatus === Status.LOADING}
>
{messages.withdraw}
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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<TableType[] | null>(null)
Expand Down Expand Up @@ -135,17 +134,11 @@ export const PayAndEarnPage = ({ tableView }: PayAndEarnPageProps) => {
contentClassName={styles.pageContainer}
header={header}
>
{!tableOptions || !selectedTable || balance === null ? (
{!tableOptions || !selectedTable ? (
<LoadingSpinner className={styles.spinner} />
) : (
<>
<USDCCard
balance={balance}
refreshing={
balanceStatus === Status.LOADING ||
recoveryStatus === Status.LOADING
}
/>
<USDCCard />
<Paper w='100%'>
<Flex direction='column' w='100%'>
<Flex
Expand Down
Loading

0 comments on commit cd72f9b

Please sign in to comment.