-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: composition table #1623
Merged
Merged
feat: composition table #1623
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
41290e5
feat: composition table
e1379a6
handle overflow
a6d69ad
ellipsis and spacing
607377b
handle for evm address
efca11a
fix logo
08ad84a
add unrealizedpl column
9812d6a
remove p&l work
a0f201e
fix sizing
7755917
fix sizing when no invest/redeem
2f0617c
store portfolio sort in state
2a7ab23
restore SortButton
7fc07c4
remove code
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Text } from '@centrifuge/fabric' | ||
import styled from 'styled-components' | ||
import { buttonActionStyles } from './styles' | ||
|
||
export const FilterButton = styled(Text)` | ||
display: flex; | ||
align-items: center; | ||
gap: 0.3em; | ||
${buttonActionStyles} | ||
` |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
148 changes: 77 additions & 71 deletions
148
centrifuge-app/src/components/Portfolio/InvestedTokens.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,95 @@ | ||
import { AccountTokenBalance, Pool } from '@centrifuge/centrifuge-js' | ||
import { formatBalance, useBalances } from '@centrifuge/centrifuge-react' | ||
import { useAddress, useBalances } from '@centrifuge/centrifuge-react' | ||
import { Box, Grid, Stack, Text } from '@centrifuge/fabric' | ||
import * as React from 'react' | ||
import { useAddress } from '../../utils/useAddress' | ||
import { usePool } from '../../utils/usePools' | ||
import { useMemo, useState } from 'react' | ||
import { useTinlakeBalances } from '../../utils/tinlake/useTinlakeBalances' | ||
import { useTinlakePools } from '../../utils/tinlake/useTinlakePools' | ||
import { usePools } from '../../utils/usePools' | ||
import { FilterButton } from '../FilterButton' | ||
import { SortChevrons } from '../SortChevrons' | ||
import { sortTokens } from './sortTokens' | ||
import { TokenListItem } from './TokenListItem' | ||
|
||
const TOKEN_ITEM_COLUMNS = `250px 200px 100px 150px 1FR` | ||
const TOKEN_ITEM_GAP = 4 | ||
export const COLUMN_GAPS = '200px 140px 140px 140px' | ||
|
||
export type SortOptions = { | ||
sortBy: 'position' | 'market-value' | ||
sortDirection: 'asc' | 'desc' | ||
} | ||
|
||
// TODO: change canInvestRedeem to default to true once the drawer is implemented | ||
export const InvestedTokens = ({ canInvestRedeem = false }) => { | ||
const [sortOptions, setSortOptions] = useState<SortOptions>({ sortBy: 'position', sortDirection: 'desc' }) | ||
|
||
export function InvestedTokens() { | ||
const address = useAddress() | ||
const balances = useBalances(address) | ||
const centBalances = useBalances(address) | ||
const { data: tinlakeBalances } = useTinlakeBalances() | ||
|
||
return !!balances?.tranches && !!balances?.tranches.length ? ( | ||
<> | ||
<Box as="article"> | ||
<Text as="h2" variant="heading2"> | ||
Portfolio Composition | ||
</Text> | ||
</Box> | ||
<Stack gap={1}> | ||
<Grid gridTemplateColumns={TOKEN_ITEM_COLUMNS} gap={TOKEN_ITEM_GAP} px={2}> | ||
const { data: tinlakePools } = useTinlakePools() | ||
const pools = usePools() | ||
|
||
const balances = useMemo(() => { | ||
return [ | ||
...(centBalances?.tranches || []), | ||
...(tinlakeBalances?.tranches.filter((tranche) => !tranche.balance.isZero) || []), | ||
] | ||
}, [centBalances, tinlakeBalances]) | ||
|
||
const sortedTokens = | ||
balances.length && pools && tinlakePools | ||
? sortTokens( | ||
balances, | ||
{ | ||
centPools: pools, | ||
tinlakePools: tinlakePools.pools, | ||
}, | ||
sortOptions | ||
) | ||
: [] | ||
|
||
const handleSort = (sortOption: SortOptions['sortBy']) => { | ||
setSortOptions((prev) => ({ | ||
sortBy: sortOption, | ||
sortDirection: prev.sortBy !== sortOption ? 'desc' : prev.sortDirection === 'asc' ? 'desc' : 'asc', | ||
})) | ||
} | ||
|
||
return sortedTokens.length ? ( | ||
<Stack as="article" gap={2}> | ||
<Text as="h2" variant="heading2"> | ||
Portfolio | ||
</Text> | ||
|
||
<Box overflow="auto"> | ||
<Grid gridTemplateColumns={COLUMN_GAPS} gap={3} alignItems="start" px={2}> | ||
<Text as="span" variant="body3"> | ||
Token | ||
</Text> | ||
<Text as="button" variant="body3"> | ||
|
||
<FilterButton forwardedAs="span" variant="body3" onClick={() => handleSort('position')}> | ||
Position | ||
</Text> | ||
<SortChevrons | ||
sorting={{ isActive: sortOptions.sortBy === 'position', direction: sortOptions.sortDirection }} | ||
/> | ||
</FilterButton> | ||
|
||
<Text as="span" variant="body3"> | ||
Token price | ||
</Text> | ||
<Text as="button" variant="body3"> | ||
Market value | ||
</Text> | ||
|
||
<FilterButton forwardedAs="span" variant="body3" onClick={() => handleSort('market-value')}> | ||
Market Value | ||
<SortChevrons | ||
sorting={{ isActive: sortOptions.sortBy === 'market-value', direction: sortOptions.sortDirection }} | ||
/> | ||
</FilterButton> | ||
</Grid> | ||
|
||
<Stack as="ul" role="list" gap={1}> | ||
{balances.tranches.map((tranche, index) => ( | ||
<Box key={`${tranche.trancheId}${index}`} as="li"> | ||
<TokenListItem {...tranche} /> | ||
</Box> | ||
<Stack as="ul" role="list" gap={1} py={1}> | ||
{balances.map((balance, index) => ( | ||
<TokenListItem key={index} canInvestRedeem={canInvestRedeem} {...balance} /> | ||
))} | ||
</Stack> | ||
</Stack> | ||
</> | ||
</Box> | ||
</Stack> | ||
) : null | ||
} | ||
|
||
type TokenCardProps = AccountTokenBalance | ||
export function TokenListItem({ balance, currency, poolId, trancheId }: TokenCardProps) { | ||
const pool = usePool(poolId) as Pool | ||
const isTinlakePool = poolId?.startsWith('0x') | ||
|
||
if (isTinlakePool) { | ||
return null | ||
} | ||
|
||
const tranche = pool.tranches.find(({ id }) => id === trancheId) | ||
|
||
return ( | ||
<Grid | ||
gridTemplateColumns={TOKEN_ITEM_COLUMNS} | ||
gap={TOKEN_ITEM_GAP} | ||
padding={2} | ||
borderStyle="solid" | ||
borderWidth={1} | ||
borderColor="borderSecondary" | ||
> | ||
<Text as="span" variant="body2"> | ||
{currency.name} | ||
</Text> | ||
|
||
<Text as="span" variant="body2"> | ||
{formatBalance(balance, tranche?.currency.symbol)} | ||
</Text> | ||
|
||
<Text as="span" variant="body2"> | ||
{tranche?.tokenPrice ? formatBalance(tranche.tokenPrice.toDecimal(), tranche.currency.symbol, 4) : '-'} | ||
</Text> | ||
|
||
<Text as="span" variant="body2"> | ||
{tranche?.tokenPrice | ||
? formatBalance(balance.toDecimal().mul(tranche.tokenPrice.toDecimal()), tranche.currency.symbol, 4) | ||
: '-'} | ||
</Text> | ||
</Grid> | ||
) | ||
} |
100 changes: 100 additions & 0 deletions
100
centrifuge-app/src/components/Portfolio/TokenListItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { AccountTokenBalance } from '@centrifuge/centrifuge-js' | ||
import { formatBalance, useCentrifuge } from '@centrifuge/centrifuge-react' | ||
import { | ||
AnchorButton, | ||
Box, | ||
Button, | ||
Grid, | ||
IconExternalLink, | ||
IconMinus, | ||
IconPlus, | ||
Shelf, | ||
Text, | ||
Thumbnail, | ||
} from '@centrifuge/fabric' | ||
import styled, { useTheme } from 'styled-components' | ||
import { usePool, usePoolMetadata } from '../../utils/usePools' | ||
import { Eththumbnail } from '../EthThumbnail' | ||
import { Root } from '../ListItemCardStyles' | ||
import { COLUMN_GAPS } from './InvestedTokens' | ||
|
||
export type TokenCardProps = AccountTokenBalance & { | ||
canInvestRedeem?: boolean | ||
} | ||
|
||
const TokenName = styled(Text)` | ||
text-wrap: nowrap; | ||
` | ||
|
||
export function TokenListItem({ balance, currency, poolId, trancheId, canInvestRedeem }: TokenCardProps) { | ||
const { sizes } = useTheme() | ||
const pool = usePool(poolId, false) | ||
const { data: metadata } = usePoolMetadata(pool) | ||
const cent = useCentrifuge() | ||
|
||
const isTinlakePool = poolId.startsWith('0x') | ||
|
||
// @ts-expect-error known typescript issue: https://github.com/microsoft/TypeScript/issues/44373 | ||
const trancheInfo = pool?.tranches.find(({ id }) => id === trancheId) | ||
const icon = metadata?.pool?.icon?.uri ? cent.metadata.parseMetadataUrl(metadata.pool.icon.uri) : null | ||
|
||
return ( | ||
<Root as="article" minWidth={canInvestRedeem ? '960px' : '750px'}> | ||
<Grid gridTemplateColumns={`${COLUMN_GAPS} 1fr`} gap={3} p={2} alignItems="center"> | ||
<Grid as="header" gridTemplateColumns={`${sizes.iconMedium}px 1fr`} alignItems="center" gap={2}> | ||
<Eththumbnail show={isTinlakePool}> | ||
{icon ? ( | ||
<Box as="img" src={icon} alt="" height="iconMedium" width="iconMedium" /> | ||
) : ( | ||
<Thumbnail type="pool" label="LP" size="small" /> | ||
)} | ||
</Eththumbnail> | ||
|
||
<TokenName textOverflow="ellipsis" variant="body2"> | ||
{currency.name} | ||
</TokenName> | ||
</Grid> | ||
|
||
<Text textOverflow="ellipsis" variant="body2"> | ||
{formatBalance(balance, currency.symbol)} | ||
</Text> | ||
|
||
<Text textOverflow="ellipsis" variant="body2"> | ||
{trancheInfo?.tokenPrice | ||
? formatBalance(trancheInfo.tokenPrice.toDecimal(), trancheInfo.currency.symbol, 4) | ||
: '-'} | ||
</Text> | ||
|
||
<Text textOverflow="ellipsis" variant="body2"> | ||
{trancheInfo?.tokenPrice | ||
? formatBalance(balance.toDecimal().mul(trancheInfo.tokenPrice.toDecimal()), trancheInfo.currency.symbol, 4) | ||
: '-'} | ||
</Text> | ||
|
||
{canInvestRedeem && ( | ||
<Shelf gap={2} justifySelf="end"> | ||
{isTinlakePool ? ( | ||
<AnchorButton | ||
variant="tertiary" | ||
icon={IconExternalLink} | ||
href="https://legacy.tinlake.centrifuge.io/portfolio" | ||
target="_blank" | ||
> | ||
View on Tinlake | ||
</AnchorButton> | ||
) : ( | ||
<> | ||
<Button variant="tertiary" icon={IconMinus}> | ||
Redeem | ||
</Button> | ||
<Button variant="tertiary" icon={IconPlus}> | ||
Invest | ||
</Button> | ||
</> | ||
)} | ||
</Shelf> | ||
)} | ||
</Grid> | ||
</Root> | ||
) | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broke out the code from
centrifuge-app/src/components/PoolFilter/styles.ts
to different components so that we can use it outside of the context ofPoolFilter
.