-
Notifications
You must be signed in to change notification settings - Fork 4
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(extension): #252: asset list #259
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'chrome-extension': minor | ||
'@repo/ui': minor | ||
--- | ||
|
||
Add AssetsTable to the home screen of the extension |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { | ||
Table, | ||
TableBody, | ||
TableCell, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
} from '@repo/ui/components/ui/table'; | ||
import { ValueViewComponent } from '@repo/ui/components/ui/value'; | ||
import { ValueView } from '@penumbra-zone/protobuf/penumbra/core/asset/v1/asset_pb'; | ||
import { getDisplayDenomFromView, getEquivalentValues } from '@penumbra-zone/getters/value-view'; | ||
import { getMetadataFromBalancesResponse } from '@penumbra-zone/getters/balances-response'; | ||
import { asValueView } from '@penumbra-zone/getters/equivalent-value'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { viewClient } from '../../../clients'; | ||
|
||
const EquivalentValues = ({ valueView }: { valueView?: ValueView }) => { | ||
const equivalentValuesAsValueViews = (getEquivalentValues.optional(valueView) ?? []).map( | ||
asValueView, | ||
); | ||
|
||
return ( | ||
<div className='flex flex-wrap gap-2'> | ||
{equivalentValuesAsValueViews.map(equivalentValueAsValueView => ( | ||
<ValueViewComponent | ||
key={getDisplayDenomFromView(equivalentValueAsValueView)} | ||
view={equivalentValueAsValueView} | ||
variant='equivalent' | ||
/> | ||
))} | ||
</div> | ||
); | ||
}; | ||
|
||
export interface AssetsTableProps { | ||
account: number; | ||
} | ||
|
||
export const AssetsTable = ({ account }: AssetsTableProps) => { | ||
const { | ||
data: balances, | ||
isLoading, | ||
error, | ||
} = useQuery({ | ||
queryKey: ['balances', account], | ||
staleTime: Infinity, | ||
queryFn: async () => { | ||
try { | ||
const balances = await Array.fromAsync(viewClient.balances({ accountFilter: { account } })); | ||
balances.sort((a, b) => { | ||
const aScore = getMetadataFromBalancesResponse.optional(a)?.priorityScore ?? 0n; | ||
const bScore = getMetadataFromBalancesResponse.optional(b)?.priorityScore ?? 0n; | ||
return Number(bScore - aScore); | ||
}); | ||
return balances; | ||
} catch (_) { | ||
return []; | ||
} | ||
}, | ||
}); | ||
|
||
if (isLoading || error || !balances?.length) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<Table> | ||
<TableHeader className='group'> | ||
<TableRow> | ||
<TableHead>Balance</TableHead> | ||
<TableHead>Value</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{balances.map((assetBalance, index) => ( | ||
<TableRow className='group' key={index}> | ||
<TableCell> | ||
<ValueViewComponent view={assetBalance.balanceView} /> | ||
</TableCell> | ||
<TableCell> | ||
<EquivalentValues valueView={assetBalance.balanceView} /> | ||
</TableCell> | ||
</TableRow> | ||
))} | ||
</TableBody> | ||
</Table> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,17 @@ | ||
import { Address, FullViewingKey } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys_pb'; | ||
import { SelectAccount } from '@repo/ui/components/ui/select'; | ||
import { getAddressByIndex, getEphemeralByIndex } from '@penumbra-zone/wasm/keys'; | ||
import { Wallet } from '@penumbra-zone/types/wallet'; | ||
import { IndexHeader } from './index-header'; | ||
import { useStore } from '../../../state'; | ||
import { BlockSync } from './block-sync'; | ||
import { localExtStorage } from '../../../storage/local'; | ||
import { getActiveWallet } from '../../../state/wallets'; | ||
import { needsLogin, needsOnboard } from '../popup-needs'; | ||
import { Address, FullViewingKey } from '@penumbra-zone/protobuf/penumbra/core/keys/v1/keys_pb'; | ||
import { getAddressByIndex, getEphemeralByIndex } from '@penumbra-zone/wasm/keys'; | ||
import { Wallet } from '@penumbra-zone/types/wallet'; | ||
import { ValidateAddress } from './validate-address'; | ||
import { FrontendLink } from './frontend-link'; | ||
import { AssetsTable } from './assets-table'; | ||
import { useState } from 'react'; | ||
|
||
export interface PopupLoaderData { | ||
fullSyncHeight?: number; | ||
|
@@ -45,23 +47,35 @@ const getAddrByIndex = | |
|
||
export const PopupIndex = () => { | ||
const activeWallet = useStore(getActiveWallet); | ||
const [index, setIndex] = useState<number>(0); | ||
|
||
return ( | ||
<> | ||
<BlockSync /> | ||
<div className='fixed inset-0 h-full bg-logoImg bg-[left_-180px] bg-no-repeat pointer-events-none' /> | ||
<div className='fixed inset-0 h-full bg-logo pointer-events-none' /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's so weird, i can't reproduce There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did a patch fix, no idea if it helps, but at least not breaks anything. Can you create a new issue if the bug persists, so somebody who can reproduce it can fix? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not necessarily blocking, feel free to merge – filed #264 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks @TalDerei merging this now |
||
|
||
<div className='flex h-full grow flex-col items-stretch gap-[15px] bg-logo bg-left-bottom px-[15px] pb-[15px]'> | ||
<IndexHeader /> | ||
<div className='z-[1] flex flex-col h-full'> | ||
<BlockSync /> | ||
|
||
<div className='flex flex-col gap-4'> | ||
{activeWallet && <SelectAccount getAddrByIndex={getAddrByIndex(activeWallet)} />} | ||
</div> | ||
<div className='flex h-full grow flex-col items-stretch gap-[15px] px-[15px] pb-[15px]'> | ||
<IndexHeader /> | ||
|
||
<ValidateAddress /> | ||
<div className='flex flex-col gap-4'> | ||
{activeWallet && ( | ||
<SelectAccount | ||
index={index} | ||
setIndex={setIndex} | ||
getAddrByIndex={getAddrByIndex(activeWallet)} | ||
/> | ||
)} | ||
</div> | ||
|
||
<div className='shrink-0 grow' /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment: removing this may be causing the layout issues when scrolling? the position should be fixed to prevent scrolling outside the bounds of the extension |
||
<ValidateAddress /> | ||
|
||
<FrontendLink /> | ||
<FrontendLink /> | ||
|
||
<AssetsTable account={index} /> | ||
</div> | ||
</div> | ||
</> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import * as React from 'react'; | ||
import { cn } from '../../../lib/utils'; | ||
|
||
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>( | ||
({ className, ...props }, ref) => ( | ||
<table ref={ref} className={cn('w-full caption-bottom text-sm', className)} {...props} /> | ||
), | ||
); | ||
Table.displayName = 'Table'; | ||
|
||
const TableHeader = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} /> | ||
)); | ||
TableHeader.displayName = 'TableHeader'; | ||
|
||
const TableBody = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} /> | ||
)); | ||
TableBody.displayName = 'TableBody'; | ||
|
||
const TableFooter = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tfoot | ||
ref={ref} | ||
className={cn('bg-primary font-medium text-primary-foreground', className)} | ||
{...props} | ||
/> | ||
)); | ||
TableFooter.displayName = 'TableFooter'; | ||
|
||
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>( | ||
({ className, ...props }, ref) => ( | ||
<tr ref={ref} className={cn('border-b border-border-secondary', className)} {...props} /> | ||
), | ||
); | ||
TableRow.displayName = 'TableRow'; | ||
|
||
const TableHead = React.forwardRef< | ||
HTMLTableCellElement, | ||
React.ThHTMLAttributes<HTMLTableCellElement> | ||
>(({ className, ...props }, ref) => ( | ||
<th | ||
ref={ref} | ||
className={cn( | ||
'py-4 text-left align-top text-lg leading-[26px] font-headline font-semibold [&:has([role=checkbox])]:pr-0', | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
)); | ||
TableHead.displayName = 'TableHead'; | ||
|
||
const TableCell = React.forwardRef< | ||
HTMLTableCellElement, | ||
React.TdHTMLAttributes<HTMLTableCellElement> | ||
>(({ className, ...props }, ref) => ( | ||
<td | ||
ref={ref} | ||
className={cn('py-4 align-middle text-base [&:has([role=checkbox])]:pr-0', className)} | ||
{...props} | ||
/> | ||
)); | ||
TableCell.displayName = 'TableCell'; | ||
|
||
const TableCaption = React.forwardRef< | ||
HTMLTableCaptionElement, | ||
React.HTMLAttributes<HTMLTableCaptionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<caption ref={ref} className={cn('mt-4 text-sm text-muted-foreground', className)} {...props} /> | ||
)); | ||
TableCaption.displayName = 'TableCaption'; | ||
|
||
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }; |
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.
suggestion: can we make UM (and optionally the fee tokens – USDC, OSMO, ATOM) the priority by ordering them higher in the balance list?