MP-3338: added highlight to changed data in AccountBalancesTable (#424)

* MP-3338: added highlight to changed data in AccountBalancesTable

# Conflicts:
#	src/components/Account/AccountBalancesTable.tsx

* tidy: refactor

* refactor accountBalancesTable components

* MP-3357: created the Intro component (#427)

* MP-3338: added highlight to changed data in AccountBalancesTable

* refactor accountBalancesTable components

* MP-3338: added highlight to changed data in AccountBalancesTable

* refactor accountBalancesTable components

* refactor accountBalancesTable components

* refactor accountBalancesTable components

* MP-3338: added highlight to changed data in AccountBalancesTable

* refactor accountBalancesTable components

* refactor accountBalancesTable components

* fix test

---------

Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com>
This commit is contained in:
Linkie Link 2023-09-06 09:17:57 +02:00 committed by GitHub
parent a3737ce584
commit b0b957a5b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 177 additions and 114 deletions

View File

@ -41,4 +41,4 @@ describe('<AccountDetails />', () => {
const container = screen.queryByTestId('account-details')
expect(container).not.toBeInTheDocument()
})
})
})

View File

@ -0,0 +1,62 @@
import { BN_ZERO } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin'
import { demagnify, getCoinValue } from 'utils/formatters'
export function getAssetAccountBalanceRow(
type: 'deposits' | 'borrowing' | 'lending',
asset: Asset,
prices: BNCoin[],
position: BNCoin,
apy: number,
prev: BNCoin,
): AccountBalanceRow {
const { amount, denom } = position
const amountChange = position.amount.minus(prev.amount)
return {
type,
symbol: asset.symbol,
size: demagnify(amount, asset),
value: getCoinValue(BNCoin.fromDenomAndBigNumber(denom, amount), prices).toString(),
denom,
amount,
apy,
amountChange,
}
}
export function getVaultAccountBalanceRow(
vault: DepositedVault,
apy: number,
prev: DepositedVault,
): AccountBalanceRow {
const { name } = vault
const totalValue = vault.values.primary.plus(vault.values.secondary)
const prevTotalValue = prev.values.primary.plus(prev.values.secondary)
const amountChange = totalValue.minus(prevTotalValue)
return {
type: 'vault',
symbol: name,
size: 0,
value: totalValue.toString(),
denom: vault.denoms.lp,
amount: BN_ZERO,
apy,
amountChange,
}
}
export function getAmountChangeColor(
type: 'deposits' | 'borrowing' | 'lending' | 'vault',
amount: BigNumber,
) {
if (type === 'borrowing') {
if (amount.isGreaterThan(0)) return 'text-loss'
if (amount.isLessThan(0)) return 'text-profit'
}
if (amount.isGreaterThan(0)) return 'text-profit'
if (amount.isLessThan(0)) return 'text-loss'
return ''
}

View File

@ -7,9 +7,11 @@ import {
useReactTable,
} from '@tanstack/react-table'
import classNames from 'classnames'
import React from 'react'
import { useMemo, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { getAmountChangeColor } from 'components/Account/AccountBalancesTable/functions'
import useAccountBalanceData from 'components/Account/AccountBalancesTable/useAccountBalanceData'
import AccountFund from 'components/Account/AccountFund'
import ActionButton from 'components/Button/ActionButton'
import DisplayCurrency from 'components/DisplayCurrency'
@ -20,14 +22,11 @@ import { ASSETS } from 'constants/assets'
import { BN_ZERO } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useCurrentAccount from 'hooks/useCurrentAccount'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { getAssetByDenom } from 'utils/assets'
import { convertLiquidityRateToAPR, demagnify, getCoinValue } from 'utils/formatters'
import { demagnify } from 'utils/formatters'
import { BN } from 'utils/helpers'
import { convertAprToApy } from 'utils/parsers'
import { getPage, getRoute } from 'utils/route'
interface Props {
@ -37,95 +36,22 @@ interface Props {
tableBodyClassName?: string
}
interface PositionValue {
type: 'deposits' | 'borrowing' | 'lending'
symbol: string
size: number
value: string
denom: string
amount: BigNumber
apy: number
}
function calculatePositionValues(
type: 'deposits' | 'borrowing' | 'lending',
asset: Asset,
prices: BNCoin[],
position: BNCoin,
apy: number,
) {
const { amount, denom } = position
return {
type,
symbol: asset.symbol,
size: demagnify(amount, asset),
value: getCoinValue(BNCoin.fromDenomAndBigNumber(denom, amount), prices).toString(),
denom,
amount: type === 'borrowing' ? amount.negated() : amount,
apy,
}
}
function calculateVaultValues(vault: DepositedVault, apy: number) {
const { name } = vault
const totalValue = vault.values.primary.plus(vault.values.secondary)
return {
type: 'vault',
symbol: name,
size: 0,
value: totalValue.toString(),
denom: vault.denoms.lp,
amount: BN_ZERO,
apy,
}
}
export default function AccountBalancesTable(props: Props) {
const { data: prices } = usePrices()
export default function Index(props: Props) {
const { account, lendingData, borrowingData, tableBodyClassName } = props
const currentAccount = useCurrentAccount()
const navigate = useNavigate()
const { pathname } = useLocation()
const address = useStore((s) => s.address)
const [sorting, setSorting] = React.useState<SortingState>([])
const balanceData = React.useMemo<AccountBalanceRow[]>(() => {
const accountDeposits = props.account?.deposits ?? []
const accountLends = props.account?.lends ?? []
const accountDebts = props.account?.debts ?? []
const accountVaults = props.account?.vaults ?? []
const [sorting, setSorting] = useState<SortingState>([])
const updatedAccount = useStore((s) => s.updatedAccount)
const accountBalanceData = useAccountBalanceData({
account,
updatedAccount,
lendingData,
borrowingData,
})
const deposits: PositionValue[] = []
accountDeposits.forEach((deposit) => {
const asset = ASSETS.find(byDenom(deposit.denom))
if (!asset) return
const apy = 0
deposits.push(calculatePositionValues('deposits', asset, prices, deposit, apy))
})
const lends = accountLends.map((lending) => {
const asset = ASSETS.find(byDenom(lending.denom)) ?? ASSETS[0]
const apr = convertLiquidityRateToAPR(
props.lendingData.find((market) => market.asset.denom === lending.denom)
?.marketLiquidityRate ?? 0,
)
const apy = convertAprToApy(apr, 365)
return calculatePositionValues('lending', asset, prices, lending, apy)
})
const vaults = accountVaults.map((vault) => {
const apy = (vault.apy ?? 0) * 100
return calculateVaultValues(vault, apy)
})
const debts = accountDebts.map((debt) => {
const asset = ASSETS.find(byDenom(debt.denom)) ?? ASSETS[0]
const apy =
props.borrowingData.find((market) => market.asset.denom === debt.denom)?.borrowRate ?? 0
return calculatePositionValues('borrowing', asset, prices, debt, apy * -100)
})
return [...deposits, ...lends, ...vaults, ...debts]
}, [prices, props.account, props.borrowingData, props.lendingData])
const columns = React.useMemo<ColumnDef<AccountBalanceRow>[]>(
const columns = useMemo<ColumnDef<AccountBalanceRow>[]>(
() => [
{
header: 'Asset',
@ -146,11 +72,12 @@ export default function AccountBalancesTable(props: Props) {
accessorKey: 'value',
id: 'value',
cell: ({ row }) => {
const color = getAmountChangeColor(row.original.type, row.original.amountChange)
const coin = new BNCoin({
denom: ORACLE_DENOM,
amount: row.original.value.toString(),
})
return <DisplayCurrency coin={coin} className='text-xs text-right' />
return <DisplayCurrency coin={coin} className={classNames('text-xs text-right', color)} />
},
},
{
@ -160,13 +87,14 @@ export default function AccountBalancesTable(props: Props) {
cell: ({ row }) => {
if (row.original.amount.isEqualTo(BN_ZERO))
return <span className='w-full text-xs text-center'>&ndash;</span>
const color = getAmountChangeColor(row.original.type, row.original.amountChange)
const amount = demagnify(
row.original.amount,
getAssetByDenom(row.original.denom) ?? ASSETS[0],
)
return (
<FormattedNumber
className='text-xs text-right'
className={classNames('text-xs text-right', color)}
amount={Number(BN(amount).abs())}
options={{ maxDecimals: 4, abbreviated: true }}
animate
@ -179,7 +107,7 @@ export default function AccountBalancesTable(props: Props) {
accessorKey: 'apy',
header: 'APY',
cell: ({ row }) => {
if (row.original.type === 'deposit')
if (row.original.type === 'deposits')
return <span className='w-full text-xs text-center'>&ndash;</span>
return (
<FormattedNumber
@ -196,7 +124,7 @@ export default function AccountBalancesTable(props: Props) {
)
const table = useReactTable({
data: balanceData,
data: accountBalanceData,
columns,
state: {
sorting,
@ -206,7 +134,7 @@ export default function AccountBalancesTable(props: Props) {
getSortedRowModel: getSortedRowModel(),
})
if (balanceData.length === 0)
if (accountBalanceData.length === 0)
return (
<div className='w-full p-4'>
<ActionButton
@ -214,8 +142,8 @@ export default function AccountBalancesTable(props: Props) {
text='Fund this Account'
color='tertiary'
onClick={() => {
if (currentAccount?.id !== props.account.id) {
navigate(getRoute(getPage(pathname), address, props.account.id))
if (currentAccount?.id !== account.id) {
navigate(getRoute(getPage(pathname), address, account.id))
}
useStore.setState({
focusComponent: {
@ -276,7 +204,7 @@ export default function AccountBalancesTable(props: Props) {
</tr>
))}
</thead>
<tbody className={props.tableBodyClassName}>
<tbody className={tableBodyClassName}>
{table.getRowModel().rows.map((row) => {
return (
<tr key={row.id} className=' text-white/60'>
@ -301,4 +229,4 @@ export default function AccountBalancesTable(props: Props) {
</tbody>
</table>
)
}
}

View File

@ -0,0 +1,74 @@
import { useMemo } from 'react'
import {
getAssetAccountBalanceRow,
getVaultAccountBalanceRow,
} from 'components/Account/AccountBalancesTable/functions'
import { ASSETS } from 'constants/assets'
import usePrices from 'hooks/usePrices'
import { byDenom } from 'utils/array'
import { convertLiquidityRateToAPR } from 'utils/formatters'
import { convertAprToApy } from 'utils/parsers'
interface Props {
account: Account
updatedAccount?: Account
lendingData: LendingMarketTableData[]
borrowingData: BorrowMarketTableData[]
}
export default function useAccountBalanceData(props: Props) {
const { account, updatedAccount, lendingData, borrowingData } = props
const { data: prices } = usePrices()
return useMemo<AccountBalanceRow[]>(() => {
const usedAccount = updatedAccount ?? account
const accountDeposits = usedAccount?.deposits ?? []
const accountLends = usedAccount?.lends ?? []
const accountDebts = usedAccount?.debts ?? []
const accountVaults = usedAccount?.vaults ?? []
const deposits: AccountBalanceRow[] = []
accountDeposits.forEach((deposit) => {
const asset = ASSETS.find(byDenom(deposit.denom))
if (!asset) return
const apy = 0
const prevDeposit = updatedAccount
? account?.deposits.find((position) => position.denom === deposit.denom) ?? deposit
: deposit
deposits.push(getAssetAccountBalanceRow('deposits', asset, prices, deposit, apy, prevDeposit))
})
const lends = accountLends.map((lending) => {
const asset = ASSETS.find(byDenom(lending.denom)) ?? ASSETS[0]
const apr = convertLiquidityRateToAPR(
lendingData.find((market) => market.asset.denom === lending.denom)?.marketLiquidityRate ??
0,
)
const apy = convertAprToApy(apr, 365)
const prevLending = updatedAccount
? account?.lends.find((position) => position.denom === lending.denom) ?? lending
: lending
return getAssetAccountBalanceRow('lending', asset, prices, lending, apy, prevLending)
})
const vaults = accountVaults.map((vault) => {
const apy = (vault.apy ?? 0) * 100
const prevVault = updatedAccount
? account?.vaults.find((position) => position.name === vault.name) ?? vault
: vault
return getVaultAccountBalanceRow(vault, apy, prevVault)
})
const debts = accountDebts.map((debt) => {
const asset = ASSETS.find(byDenom(debt.denom)) ?? ASSETS[0]
const apy = borrowingData.find((market) => market.asset.denom === debt.denom)?.borrowRate ?? 0
const prevDebt = updatedAccount
? account?.debts.find((position) => position.denom === debt.denom) ?? debt
: debt
return getAssetAccountBalanceRow('borrowing', asset, prices, debt, apy * -100, prevDebt)
})
return [...deposits, ...lends, ...vaults, ...debts]
}, [prices, account, updatedAccount, borrowingData, lendingData])
}

View File

@ -146,7 +146,7 @@ function AccountDetails(props: Props) {
<AccountComposition account={account} />
<Text className='w-full px-4 py-2 text-white bg-white/10'>Balances</Text>
<AccountBalancesTable
account={updatedAccount ? updatedAccount : account}
account={account}
borrowingData={borrowAssetsData}
lendingData={lendingAssetsData}
/>
@ -154,4 +154,4 @@ function AccountDetails(props: Props) {
</div>
</div>
)
}
}

View File

@ -89,4 +89,4 @@ export default function AccountOverview() {
<Content />
</Suspense>
)
}
}

View File

@ -90,7 +90,7 @@ export default function AccountSummary(props: Props) {
renderContent: () =>
props.account ? (
<AccountBalancesTable
account={updatedAccount ? updatedAccount : props.account}
account={props.account}
borrowingData={borrowAssetsData}
lendingData={lendingAssetsData}
/>
@ -126,4 +126,4 @@ function Item(props: ItemProps) {
<div className='flex h-4.5 w-full'>{props.children}</div>
</div>
)
}
}

View File

@ -5,11 +5,9 @@ import Card from 'components/Card'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import useStore from 'store'
export default function AccountDetailsCard() {
const account = useCurrentAccount()
const updatedAccount = useStore((s) => s.updatedAccount)
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
useBorrowMarketAssetsTableData()
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
@ -33,7 +31,7 @@ export default function AccountDetailsCard() {
return (
<Card className='h-fit' title={tabs}>
<AccountBalancesTable
account={updatedAccount || account}
account={account}
borrowingData={borrowAssetsData}
lendingData={lendingAssetsData}
tableBodyClassName='gradient-card-content'
@ -45,4 +43,4 @@ export default function AccountDetailsCard() {
const className = {
tabWrapper: 'flex w-full items-center bg-white/10 pt-4 pl-4 font-semibold',
tab: 'mr-4 pb-3 cursor-pointer select-none flex flex-row border-b-2 border-pink border-solid',
}
}

View File

@ -255,4 +255,4 @@ export class OsmosisTheGraphDataFeed implements IDatafeedChartApi {
unsubscribeBars(listenerGuid: string): void {
// TheGraph doesn't support websockets yet
}
}
}

View File

@ -14,11 +14,12 @@ interface AccountChange {
}
interface AccountBalanceRow {
type: string
symbol: string
denom: string
amount: BigNumber
value: string | number
size: number
apy: number
denom: string
size: number
symbol: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
value: string
amountChange: BigNumber
}