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:
parent
a3737ce584
commit
b0b957a5b3
@ -41,4 +41,4 @@ describe('<AccountDetails />', () => {
|
||||
const container = screen.queryByTestId('account-details')
|
||||
expect(container).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
62
src/components/Account/AccountBalancesTable/functions.ts
Normal file
62
src/components/Account/AccountBalancesTable/functions.ts
Normal 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 ''
|
||||
}
|
@ -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'>–</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'>–</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>
|
||||
)
|
||||
}
|
||||
}
|
@ -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])
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
@ -89,4 +89,4 @@ export default function AccountOverview() {
|
||||
<Content />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
@ -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',
|
||||
}
|
||||
}
|
@ -255,4 +255,4 @@ export class OsmosisTheGraphDataFeed implements IDatafeedChartApi {
|
||||
unsubscribeBars(listenerGuid: string): void {
|
||||
// TheGraph doesn't support websockets yet
|
||||
}
|
||||
}
|
||||
}
|
11
src/types/interfaces/account.d.ts
vendored
11
src/types/interfaces/account.d.ts
vendored
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user