From 0c7f39a6a72d16522b734292c2d7c3d9a54c6498 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Thu, 26 Oct 2023 16:19:59 +0200 Subject: [PATCH] Refactor balances table (#590) * env: update env.example after last sync * tidy: refactored AccountBalancesTable * fix: updated isCard to hideCard --- .env.example | 6 +- .../AccountBalancesTable/Columns/Apy.tsx | 28 ++ .../AccountBalancesTable/Columns/Asset.tsx | 22 ++ .../AccountBalancesTable/Columns/Size.tsx | 60 ++++ .../AccountBalancesTable/Columns/Value.tsx | 33 +++ .../Columns/useAccountBalancesColumns.tsx | 62 ++++ .../Account/AccountBalancesTable/index.tsx | 267 +++--------------- .../Account/AccountDetails/index.tsx | 1 + src/components/Account/AccountSummary.tsx | 1 + .../Table/Columns/useAvailableColumns.tsx | 3 +- .../HLS/Farm/AvailableHLSVaults.tsx | 3 +- src/components/Portfolio/Account/Balances.tsx | 1 + src/components/Table/Row.tsx | 20 +- src/components/Table/index.tsx | 38 ++- src/components/Trade/AccountDetailsCard.tsx | 15 +- src/utils/accounts.ts | 3 +- 16 files changed, 314 insertions(+), 249 deletions(-) create mode 100644 src/components/Account/AccountBalancesTable/Columns/Apy.tsx create mode 100644 src/components/Account/AccountBalancesTable/Columns/Asset.tsx create mode 100644 src/components/Account/AccountBalancesTable/Columns/Size.tsx create mode 100644 src/components/Account/AccountBalancesTable/Columns/Value.tsx create mode 100644 src/components/Account/AccountBalancesTable/Columns/useAccountBalancesColumns.tsx diff --git a/.env.example b/.env.example index 25226c89..5c508803 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,6 @@ NEXT_PUBLIC_CHAIN_ID=devnet NEXT_PUBLIC_RPC=https://rpc.devnet.osmosis.zone/ NEXT_PUBLIC_GQL=https://devnet-osmosis-gql.marsprotocol.io/graphql NEXT_PUBLIC_REST=https://lcd.devnet.osmosis.zone/ -NEXT_PUBLIC_ZAPPER=osmo1yhh8mhthj5jn5c6ty59z3tpsk554qxmlkrkcderw6jls0pcg8zxsdjdj94 -NEXT_PUBLIC_PARAMS=osmo1aye5qcer5n52crrkaf35jprsad2807q6kg3eeeu7k79h4slxfausfqhc9y # MAINNET # @@ -14,8 +12,6 @@ NEXT_PUBLIC_CHAIN_ID=osmosis-1 NEXT_PUBLIC_RPC=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-rpc-front/ NEXT_PUBLIC_GQL=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-hive-front/graphql NEXT_PUBLIC_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-lcd-front/ -NEXT_PUBLIC_ZAPPER=osmo17qwvc70pzc9mudr8t02t3pl74hhqsgwnskl734p4hug3s8mkerdqzduf7c -NEXT_PUBLIC_PARAMS=osmo1nlmdxt9ctql2jr47qd4fpgzg84cjswxyw6q99u4y4u4q6c2f5ksq7ysent # COMMON # @@ -28,6 +24,8 @@ NEXT_PUBLIC_CREDIT_MANAGER=osmo1f2m24wktq0sw3c0lexlg7fv4kngwyttvzws3a3r3al9ld2s2 NEXT_PUBLIC_INCENTIVES=osmo1nkahswfr8shg8rlxqwup0vgahp0dk4x8w6tkv3rra8rratnut36sk22vrm NEXT_PUBLIC_SWAPPER=osmo1wee0z8c7tcawyl647eapqs4a88q8jpa7ddy6nn2nrs7t47p2zhxswetwla NEXT_PUBLIC_PYTH=osmo13ge29x4e2s63a8ytz2px8gurtyznmue4a69n5275692v3qn3ks8q7cwck7 +NEXT_PUBLIC_ZAPPER=osmo17qwvc70pzc9mudr8t02t3pl74hhqsgwnskl734p4hug3s8mkerdqzduf7c +NEXT_PUBLIC_PARAMS=osmo1nlmdxt9ctql2jr47qd4fpgzg84cjswxyw6q99u4y4u4q6c2f5ksq7ysent NEXT_PUBLIC_PYTH_ENDPOINT=https://hermes.pyth.network/api NEXT_PUBLIC_MAINNET_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-rpc-front/ NEXT_PUBLIC_CANDLES_ENDPOINT=https://osmosis-candles.marsprotocol.io/ diff --git a/src/components/Account/AccountBalancesTable/Columns/Apy.tsx b/src/components/Account/AccountBalancesTable/Columns/Apy.tsx new file mode 100644 index 00000000..03321a6c --- /dev/null +++ b/src/components/Account/AccountBalancesTable/Columns/Apy.tsx @@ -0,0 +1,28 @@ +import AssetRate from 'components/Asset/AssetRate' +import { byDenom } from 'utils/array' + +export const APY_META = { accessorKey: 'apy', header: 'APY' } + +interface Props { + apy: number + markets: Market[] + denom: string + type: 'deposits' | 'borrowing' | 'lending' | 'vault' +} + +export default function Apr(props: Props) { + const { markets, type, denom, apy } = props + + if (type === 'deposits') return

+ const isEnabled = markets.find(byDenom(denom))?.borrowEnabled ?? false + + return ( + + ) +} diff --git a/src/components/Account/AccountBalancesTable/Columns/Asset.tsx b/src/components/Account/AccountBalancesTable/Columns/Asset.tsx new file mode 100644 index 00000000..fc26c5a5 --- /dev/null +++ b/src/components/Account/AccountBalancesTable/Columns/Asset.tsx @@ -0,0 +1,22 @@ +import Text from 'components/Text' +export const ASSET_META = { accessorKey: 'symbol', header: 'Asset', id: 'symbol' } + +interface Props { + symbol: string + type: 'deposits' | 'borrowing' | 'lending' | 'vault' +} + +export const borderColor = (type: Props['type']): string => + type === 'borrowing' ? 'border-loss' : 'border-profit' + +export default function Asset(props: Props) { + const { symbol, type } = props + return ( + + {symbol} + {type === 'borrowing' && (debt)} + {type === 'lending' && (lent)} + {type === 'vault' && (farm)} + + ) +} diff --git a/src/components/Account/AccountBalancesTable/Columns/Size.tsx b/src/components/Account/AccountBalancesTable/Columns/Size.tsx new file mode 100644 index 00000000..cc65876e --- /dev/null +++ b/src/components/Account/AccountBalancesTable/Columns/Size.tsx @@ -0,0 +1,60 @@ +import { Row } from '@tanstack/react-table' +import classNames from 'classnames' + +import { getAmountChangeColor } from 'components/Account/AccountBalancesTable/functions' +import { FormattedNumber } from 'components/FormattedNumber' +import { MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math' +import { formatAmountToPrecision } from 'utils/formatters' + +export const SIZE_META = { accessorKey: 'size', header: 'Size' } + +interface Props { + size: number + amountChange: BigNumber + denom: string + type: 'deposits' | 'borrowing' | 'lending' | 'vault' +} + +export const sizeSortingFn = (a: Row, b: Row): number => { + const isVaultA = a.original.type === 'vault' + const isVaultB = b.original.type === 'vault' + + const sizeA = isVaultA ? 0 : a.original.size + const sizeB = isVaultB ? 0 : b.original.size + + return sizeA - sizeB +} + +export default function Size(props: Props) { + const { amountChange, type, size } = props + + if (type === 'vault') return

+ + const color = getAmountChangeColor(type, amountChange) + const className = classNames('text-xs text-right', color) + + if (size >= 1) + return ( + + ) + + const formattedAmount = formatAmountToPrecision(size, MAX_AMOUNT_DECIMALS) + const lowAmount = formattedAmount === 0 ? 0 : Math.max(formattedAmount, MIN_AMOUNT) + return ( + + ) +} diff --git a/src/components/Account/AccountBalancesTable/Columns/Value.tsx b/src/components/Account/AccountBalancesTable/Columns/Value.tsx new file mode 100644 index 00000000..54094b97 --- /dev/null +++ b/src/components/Account/AccountBalancesTable/Columns/Value.tsx @@ -0,0 +1,33 @@ +import { Row } from '@tanstack/react-table' +import classNames from 'classnames' + +import { getAmountChangeColor } from 'components/Account/AccountBalancesTable/functions' +import DisplayCurrency from 'components/DisplayCurrency' +import { ORACLE_DENOM } from 'constants/oracle' +import { BNCoin } from 'types/classes/BNCoin' +import { BN } from 'utils/helpers' + +export const VALUE_META = { accessorKey: 'value', header: 'Value' } + +interface Props { + amountChange: BigNumber + value: string + type: 'deposits' | 'borrowing' | 'lending' | 'vault' +} + +export const valueSortingFn = (a: Row, b: Row): number => { + const valueA = BN(a.original.value) + const valueB = BN(b.original.value) + return valueA.minus(valueB).toNumber() +} + +export default function Value(props: Props) { + const { amountChange, type, value } = props + const color = getAmountChangeColor(type, amountChange) + const coin = new BNCoin({ + denom: ORACLE_DENOM, + amount: value, + }) + + return +} diff --git a/src/components/Account/AccountBalancesTable/Columns/useAccountBalancesColumns.tsx b/src/components/Account/AccountBalancesTable/Columns/useAccountBalancesColumns.tsx new file mode 100644 index 00000000..c8cfa9e3 --- /dev/null +++ b/src/components/Account/AccountBalancesTable/Columns/useAccountBalancesColumns.tsx @@ -0,0 +1,62 @@ +import { ColumnDef } from '@tanstack/react-table' +import { useMemo } from 'react' + +import Apy, { APY_META } from 'components/Account/AccountBalancesTable/Columns/Apy' +import Asset, { ASSET_META } from 'components/Account/AccountBalancesTable/Columns/Asset' +import Size, { + SIZE_META, + sizeSortingFn, +} from 'components/Account/AccountBalancesTable/Columns/Size' +import Value, { + VALUE_META, + valueSortingFn, +} from 'components/Account/AccountBalancesTable/Columns/Value' +import useMarketAssets from 'hooks/useMarketAssets' + +export default function useAccountBalancesColumns() { + const { data: markets } = useMarketAssets() + + return useMemo[]>(() => { + return [ + { + ...ASSET_META, + cell: ({ row }) => , + }, + { + ...VALUE_META, + cell: ({ row }) => ( + + ), + + sortingFn: valueSortingFn, + }, + { + ...SIZE_META, + cell: ({ row }) => ( + + ), + sortingFn: sizeSortingFn, + }, + { + ...APY_META, + cell: ({ row }) => ( + + ), + }, + ] + }, [markets]) +} diff --git a/src/components/Account/AccountBalancesTable/index.tsx b/src/components/Account/AccountBalancesTable/index.tsx index 334e4d18..7c8d0916 100644 --- a/src/components/Account/AccountBalancesTable/index.tsx +++ b/src/components/Account/AccountBalancesTable/index.tsx @@ -1,51 +1,31 @@ -import { - ColumnDef, - flexRender, - getCoreRowModel, - getSortedRowModel, - SortingState, - useReactTable, -} from '@tanstack/react-table' import classNames from 'classnames' -import { useMemo, useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' -import { getAmountChangeColor } from 'components/Account/AccountBalancesTable/functions' +import useAccountBalancesColumns from 'components/Account/AccountBalancesTable/Columns/useAccountBalancesColumns' import useAccountBalanceData from 'components/Account/AccountBalancesTable/useAccountBalanceData' import AccountFundFullPage from 'components/Account/AccountFund/AccountFundFullPage' -import AssetRate from 'components/Asset/AssetRate' import ActionButton from 'components/Button/ActionButton' -import DisplayCurrency from 'components/DisplayCurrency' -import { FormattedNumber } from 'components/FormattedNumber' -import { SortAsc, SortDesc, SortNone } from 'components/Icons' -import Text from 'components/Text' -import { ASSETS } from 'constants/assets' -import { MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math' -import { ORACLE_DENOM } from 'constants/oracle' +import Card from 'components/Card' +import Table from 'components/Table' +import ConditionalWrapper from 'hocs/ConditionalWrapper' import useCurrentAccount from 'hooks/useCurrentAccount' -import useMarketAssets from 'hooks/useMarketAssets' import useStore from 'store' -import { BNCoin } from 'types/classes/BNCoin' -import { byDenom } from 'utils/array' -import { getAssetByDenom } from 'utils/assets' -import { demagnify, formatAmountToPrecision } from 'utils/formatters' import { getPage, getRoute } from 'utils/route' interface Props { account: Account lendingData: LendingMarketTableData[] borrowingData: BorrowMarketTableData[] + hideCard?: boolean tableBodyClassName?: string } -export default function Index(props: Props) { - const { account, lendingData, borrowingData, tableBodyClassName } = props - const { data: markets } = useMarketAssets() +export default function AccountBalancesTable(props: Props) { + const { account, lendingData, borrowingData, tableBodyClassName, hideCard } = props const currentAccount = useCurrentAccount() const navigate = useNavigate() const { pathname } = useLocation() const address = useStore((s) => s.address) - const [sorting, setSorting] = useState([]) const updatedAccount = useStore((s) => s.updatedAccount) const accountBalanceData = useAccountBalanceData({ account, @@ -54,206 +34,51 @@ export default function Index(props: Props) { borrowingData, }) - const columns = useMemo[]>( - () => [ - { - header: 'Asset', - accessorKey: 'symbol', - id: 'symbol', - cell: ({ row }) => { - return ( - - {row.original.symbol} - {row.original.type === 'borrowing' && (debt)} - {row.original.type === 'lending' && (lent)} - {row.original.type === 'vault' && (farm)} - - ) - }, - }, - { - header: 'Value', - 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 - }, - }, - { - id: 'size', - accessorKey: 'size', - header: 'Size', - cell: ({ row }) => { - const asset = getAssetByDenom(row.original.denom) - - if (row.original.type === 'vault' || !asset) - return

- - const color = getAmountChangeColor(row.original.type, row.original.amountChange) - const className = classNames('text-xs text-right', color) - const amount = demagnify( - row.original.amount, - getAssetByDenom(row.original.denom) ?? ASSETS[0], - ) - if (amount >= 1) - return ( - - ) - - const formattedAmount = formatAmountToPrecision(amount, MAX_AMOUNT_DECIMALS) - const lowAmount = formattedAmount === 0 ? 0 : Math.max(formattedAmount, MIN_AMOUNT) - return ( - - ) - }, - }, - { - id: 'apy', - accessorKey: 'apy', - header: 'APY', - cell: ({ row }) => { - if (row.original.type === 'deposits') - return

- const isEnabled = markets.find(byDenom(row.original.denom))?.borrowEnabled ?? false - return ( - - ) - }, - }, - ], - [markets], - ) - - const table = useReactTable({ - data: accountBalanceData, - columns, - state: { - sorting, - }, - onSortingChange: setSorting, - getCoreRowModel: getCoreRowModel(), - getSortedRowModel: getSortedRowModel(), - }) + const columns = useAccountBalancesColumns() if (accountBalanceData.length === 0) return ( -
- { - if (currentAccount?.id !== account.id) { - navigate(getRoute(getPage(pathname), address, account.id)) - } - useStore.setState({ - focusComponent: { - component: , - onClose: () => { - useStore.setState({ getStartedModal: true }) + ( + + {children} + + )} + > +
+ { + if (currentAccount?.id !== account.id) { + navigate(getRoute(getPage(pathname), address, account.id)) + } + useStore.setState({ + focusComponent: { + component: , + onClose: () => { + useStore.setState({ getStartedModal: true }) + }, }, - }, - }) - }} - /> -
+ }) + }} + /> +
+ ) return ( - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - ) - })} - - ))} - - - {table.getRowModel().rows.map((row) => { - return ( - - {row.getVisibleCells().map((cell) => { - const borderClass = - cell.row.original.type === 'borrowing' ? 'border-loss' : 'border-profit' - return ( - - ) - })} - - ) - })} - -
-
- - {header.column.getCanSort() - ? { - asc: , - desc: , - false: , - }[header.column.getIsSorted() as string] ?? null - : null} - - - {flexRender(header.column.columnDef.header, header.getContext())} - -
-
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
+ ) } diff --git a/src/components/Account/AccountDetails/index.tsx b/src/components/Account/AccountDetails/index.tsx index 8ef7122a..f4a26cd9 100644 --- a/src/components/Account/AccountDetails/index.tsx +++ b/src/components/Account/AccountDetails/index.tsx @@ -180,6 +180,7 @@ function AccountDetails(props: Props) { account={account} borrowingData={borrowAssetsData} lendingData={lendingAssetsData} + hideCard /> diff --git a/src/components/Account/AccountSummary.tsx b/src/components/Account/AccountSummary.tsx index 048ba2b1..57983300 100644 --- a/src/components/Account/AccountSummary.tsx +++ b/src/components/Account/AccountSummary.tsx @@ -130,6 +130,7 @@ export default function AccountSummary(props: Props) { account={props.account} borrowingData={borrowAssetsData} lendingData={lendingAssetsData} + hideCard /> ) : null, isOpen: accountSummaryTabs[1], diff --git a/src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx b/src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx index 132ab70d..dc81f9cc 100644 --- a/src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx +++ b/src/components/Earn/Farm/Table/Columns/useAvailableColumns.tsx @@ -10,8 +10,7 @@ import DepositCap, { import MaxLTV, { LTV_MAX_META } from 'components/Earn/Farm/Table/Columns/MaxLTV' import Name, { NAME_META } from 'components/Earn/Farm/Table/Columns/Name' import TVL, { TVL_META } from 'components/Earn/Farm/Table/Columns/TVL' - -import { DETAILS_META } from './Details' +import { DETAILS_META } from 'components/Earn/Farm/Table/Columns/Details' interface Props { isLoading: boolean diff --git a/src/components/HLS/Farm/AvailableHLSVaults.tsx b/src/components/HLS/Farm/AvailableHLSVaults.tsx index 6ab4036d..59800e8b 100644 --- a/src/components/HLS/Farm/AvailableHLSVaults.tsx +++ b/src/components/HLS/Farm/AvailableHLSVaults.tsx @@ -1,6 +1,7 @@ import { Suspense, useMemo } from 'react' import { NAME_META } from 'components/Earn/Farm/Table/Columns/Name' +import useAvailableColumns from 'components/Earn/Farm/Table/Columns/useAvailableColumns' import Table from 'components/Table' import { ENV } from 'constants/env' import { BN_ZERO } from 'constants/math' @@ -8,8 +9,6 @@ import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults' import useVaults from 'hooks/useVaults' import { NETWORK } from 'types/enums/network' -import useAvailableColumns from './Table/Columns/useAvailableColumns' - const title = 'Available HLS Vaults' function Content() { diff --git a/src/components/Portfolio/Account/Balances.tsx b/src/components/Portfolio/Account/Balances.tsx index e02be24a..755ec53c 100644 --- a/src/components/Portfolio/Account/Balances.tsx +++ b/src/components/Portfolio/Account/Balances.tsx @@ -29,6 +29,7 @@ function Content(props: Props) { account={account} borrowingData={borrowAssets} lendingData={lendingAssets} + hideCard /> ) diff --git a/src/components/Table/Row.tsx b/src/components/Table/Row.tsx index f7220281..0138f9e4 100644 --- a/src/components/Table/Row.tsx +++ b/src/components/Table/Row.tsx @@ -7,6 +7,12 @@ interface Props { renderExpanded?: (row: TanstackRow, table: TanstackTable) => JSX.Element rowClassName?: string rowClickHandler?: () => void + spacingClassName?: string + isBalancesTable?: boolean +} + +function getBorderColor(row: AccountBalanceRow) { + return row.type === 'borrowing' ? 'border-loss' : 'border-profit' } export default function Row(props: Props) { @@ -27,8 +33,20 @@ export default function Row(props: Props) { }} > {props.row.getVisibleCells().map((cell) => { + const isSymbolOrName = cell.column.id === 'symbol' || cell.column.id === 'name' + const borderClasses = + props.isBalancesTable && isSymbolOrName + ? classNames('border-l', getBorderColor(cell.row.original as AccountBalanceRow)) + : '' return ( - ) diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index 99145ecc..62aa21ca 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -15,6 +15,7 @@ import Card from 'components/Card' import { SortAsc, SortDesc, SortNone } from 'components/Icons' import Row from 'components/Table/Row' import Text from 'components/Text' +import ConditionalWrapper from 'hocs/ConditionalWrapper' interface Props { title: string @@ -22,6 +23,10 @@ interface Props { data: T[] initialSorting: SortingState renderExpanded?: (row: TanstackRow, table: TanstackTable) => JSX.Element + tableBodyClassName?: string + spacingClassName?: string + isBalancesTable?: boolean + hideCard?: boolean } export default function Table(props: Props) { @@ -39,9 +44,19 @@ export default function Table(props: Props) { }) return ( - -
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
- + ( + + {children} + + )} + > +
+ {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { @@ -50,7 +65,7 @@ export default function Table(props: Props) { key={header.id} onClick={header.column.getToggleSortingHandler()} className={classNames( - 'px-4 py-3', + props.spacingClassName ?? 'px-4 py-3', header.column.getCanSort() && 'hover:cursor-pointer', header.id === 'symbol' || header.id === 'name' ? 'text-left' : 'text-right', )} @@ -75,8 +90,8 @@ export default function Table(props: Props) { {flexRender(header.column.columnDef.header, header.getContext())} @@ -89,10 +104,17 @@ export default function Table(props: Props) { {table.getRowModel().rows.map((row) => ( - + ))}
- + ) } diff --git a/src/components/Trade/AccountDetailsCard.tsx b/src/components/Trade/AccountDetailsCard.tsx index 8a6d6831..b091a11c 100644 --- a/src/components/Trade/AccountDetailsCard.tsx +++ b/src/components/Trade/AccountDetailsCard.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react' import AccountBalancesTable from 'components/Account/AccountBalancesTable' -import Card from 'components/Card' import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' import useCurrentAccount from 'hooks/useCurrentAccount' import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' @@ -19,13 +18,11 @@ export default function AccountDetailsCard() { if (account) return ( - - - + ) } diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts index c129fb4a..e576ada4 100644 --- a/src/utils/accounts.ts +++ b/src/utils/accounts.ts @@ -10,8 +10,7 @@ import { import { byDenom } from 'utils/array' import { getAssetByDenom } from 'utils/assets' import { BN } from 'utils/helpers' - -import { convertApyToApr } from './parsers' +import { convertApyToApr } from 'utils/parsers' export const calculateAccountBalanceValue = ( account: Account | AccountChange,