-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement the v2 assets/transactions pages using PenumbraUI (#1607)
* Build initial assets/transaction pages * Set actionType * Create a Display component * Tweak the Display story * Add Display to the minifront v2 root * Make 0-column option possible * Tweak layout size for dashboard * Make the card title dynamic * Add a space between the tabs and content * Make valueView optional * Remove duplicate style * Set box sizing * Fix width * Tweak text * Allow fixed-width tables * Build out the assets page * Tweak design of trade link * Tweak Button docs * Add background gradient to Card * Build out transactions page * Add a title; fix cell width * Add rich titles for assets and transactions * Make some updates to the theme * Overhaul Dialog to better match the Figma mockups * Define tabs options once * Extract a hexOpacity helper * Add doc block * Fix spacing issue * Fix docs
- Loading branch information
1 parent
b7d5f21
commit ec19b33
Showing
21 changed files
with
589 additions
and
91 deletions.
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
24 changes: 24 additions & 0 deletions
24
apps/minifront/src/components/v2/dashboard-layout/assets-card-title.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,24 @@ | ||
import { Button } from '@repo/ui/Button'; | ||
import { Dialog } from '@repo/ui/Dialog'; | ||
import { Text } from '@repo/ui/Text'; | ||
import { Info } from 'lucide-react'; | ||
|
||
export const AssetsCardTitle = () => ( | ||
<div className='flex items-center gap-2'> | ||
Asset Balances | ||
<Dialog> | ||
<Dialog.Trigger asChild> | ||
<Button icon={Info} iconOnly='adornment'> | ||
Info | ||
</Button> | ||
</Dialog.Trigger> | ||
<Dialog.Content title='Asset Balances'> | ||
<Text> | ||
Your balances are shielded, and are known only to you. They are not visible on chain. Each | ||
Penumbra wallet controls many numbered accounts, each with its own balance. Account | ||
information is never revealed on-chain. | ||
</Text> | ||
</Dialog.Content> | ||
</Dialog> | ||
</div> | ||
); |
23 changes: 23 additions & 0 deletions
23
apps/minifront/src/components/v2/dashboard-layout/assets-page/equivalent-values.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,23 @@ | ||
import { ValueView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb.js'; | ||
import { asValueView } from '@penumbra-zone/getters/equivalent-value'; | ||
import { getDisplayDenomFromView, getEquivalentValues } from '@penumbra-zone/getters/value-view'; | ||
import { ValueViewComponent } from '@repo/ui/ValueViewComponent'; | ||
|
||
export 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)} | ||
valueView={equivalentValueAsValueView} | ||
priority='secondary' | ||
context='table' | ||
/> | ||
))} | ||
</div> | ||
); | ||
}; |
76 changes: 76 additions & 0 deletions
76
apps/minifront/src/components/v2/dashboard-layout/assets-page/index.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,76 @@ | ||
import { Density } from '@repo/ui/Density'; | ||
import { Table } from '@repo/ui/Table'; | ||
import { BalancesByAccount, groupByAccount, useBalancesResponses } from '../../../../state/shared'; | ||
import { shouldDisplay } from '../../../../fetchers/balances/should-display'; | ||
import { sortByPriorityScore } from '../../../../fetchers/balances/by-priority-score'; | ||
import { BalancesResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; | ||
import { getMetadataFromBalancesResponseOptional } from '@penumbra-zone/getters/balances-response'; | ||
import { PagePath } from '../../../metadata/paths'; | ||
import { getAddressIndex } from '@penumbra-zone/getters/address-view'; | ||
import { AbridgedZQueryState } from '@penumbra-zone/zquery/src/types'; | ||
import { ValueViewComponent } from '@repo/ui/ValueViewComponent'; | ||
import { EquivalentValues } from './equivalent-values'; | ||
import { TableTitle } from './table-title'; | ||
import { Link } from 'react-router-dom'; | ||
import { Button } from '@repo/ui/Button'; | ||
import { ArrowRightLeft } from 'lucide-react'; | ||
|
||
const getTradeLink = (balance: BalancesResponse): string => { | ||
const metadata = getMetadataFromBalancesResponseOptional(balance); | ||
const accountIndex = getAddressIndex(balance.accountAddress).account; | ||
const accountQuery = accountIndex ? `&account=${accountIndex}` : ''; | ||
return metadata ? `${PagePath.SWAP}?from=${metadata.symbol}${accountQuery}` : PagePath.SWAP; | ||
}; | ||
|
||
const filteredBalancesByAccountSelector = ( | ||
zQueryState: AbridgedZQueryState<BalancesResponse[]>, | ||
): BalancesByAccount[] => | ||
zQueryState.data?.filter(shouldDisplay).sort(sortByPriorityScore).reduce(groupByAccount, []) ?? | ||
[]; | ||
|
||
const BUTTON_CELL_WIDTH_PX = '56px'; | ||
|
||
export const AssetsPage = () => { | ||
const balancesByAccount = useBalancesResponses({ | ||
select: filteredBalancesByAccountSelector, | ||
shouldReselect: (before, after) => before?.data !== after.data, | ||
}); | ||
|
||
return ( | ||
<div className='flex flex-col gap-1'> | ||
{balancesByAccount?.map(account => ( | ||
<Table key={account.account} layout='fixed' title={<TableTitle account={account} />}> | ||
<Table.Thead> | ||
<Table.Tr> | ||
<Table.Th width={`calc(50% - (${BUTTON_CELL_WIDTH_PX} / 2))`}>Asset</Table.Th> | ||
<Table.Th width={`calc(50% - (${BUTTON_CELL_WIDTH_PX} / 2))`}>Estimate</Table.Th> | ||
<Table.Th width={BUTTON_CELL_WIDTH_PX} /> | ||
</Table.Tr> | ||
</Table.Thead> | ||
<Table.Tbody> | ||
{account.balances.map((balance, index) => ( | ||
<Table.Tr key={index}> | ||
<Table.Td vAlign='top'> | ||
<ValueViewComponent valueView={balance.balanceView} context='table' /> | ||
</Table.Td> | ||
<Table.Td vAlign='top'> | ||
<EquivalentValues valueView={balance.balanceView} /> | ||
</Table.Td> | ||
|
||
<Table.Td> | ||
<Link to={getTradeLink(balance)}> | ||
<Density compact> | ||
<Button icon={ArrowRightLeft} iconOnly> | ||
Trade | ||
</Button> | ||
</Density> | ||
</Link> | ||
</Table.Td> | ||
</Table.Tr> | ||
))} | ||
</Table.Tbody> | ||
</Table> | ||
))} | ||
</div> | ||
); | ||
}; |
22 changes: 22 additions & 0 deletions
22
apps/minifront/src/components/v2/dashboard-layout/assets-page/table-title.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,22 @@ | ||
import { AddressViewComponent } from '@repo/ui/AddressViewComponent'; | ||
import { BalancesByAccount } from '../../../../state/shared'; | ||
import { AddressView } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/keys/v1/keys_pb'; | ||
import { useMemo } from 'react'; | ||
|
||
export const TableTitle = ({ account }: { account: BalancesByAccount }) => { | ||
const addressView = useMemo( | ||
() => | ||
new AddressView({ | ||
addressView: { | ||
case: 'decoded', | ||
value: { | ||
address: account.address, | ||
index: { account: account.account }, | ||
}, | ||
}, | ||
}), | ||
[account.address, account.account], | ||
); | ||
|
||
return <AddressViewComponent addressView={addressView} />; | ||
}; |
49 changes: 49 additions & 0 deletions
49
apps/minifront/src/components/v2/dashboard-layout/index.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,49 @@ | ||
import { Card } from '@repo/ui/Card'; | ||
import { Outlet, useNavigate } from 'react-router-dom'; | ||
import { Grid } from '@repo/ui/Grid'; | ||
import { Tabs } from '@repo/ui/Tabs'; | ||
import { usePagePath } from '../../../fetchers/page-path'; | ||
import { PagePath } from '../../metadata/paths'; | ||
import { AssetsCardTitle } from './assets-card-title'; | ||
import { TransactionsCardTitle } from './transactions-card-title'; | ||
|
||
/** @todo: Remove this function and its uses after we switch to v2 layout */ | ||
const v2PathPrefix = (path: string) => `/v2${path}`; | ||
|
||
const CARD_TITLE_BY_PATH = { | ||
[v2PathPrefix(PagePath.DASHBOARD)]: <AssetsCardTitle />, | ||
[v2PathPrefix(PagePath.TRANSACTIONS)]: <TransactionsCardTitle />, | ||
}; | ||
|
||
const TABS_OPTIONS = [ | ||
{ label: 'Assets', value: v2PathPrefix(PagePath.DASHBOARD) }, | ||
{ label: 'Transactions', value: v2PathPrefix(PagePath.TRANSACTIONS) }, | ||
]; | ||
|
||
export const DashboardLayout = () => { | ||
const pagePath = usePagePath(); | ||
const navigate = useNavigate(); | ||
|
||
return ( | ||
<Grid container> | ||
<Grid mobile={0} tablet={2} desktop={3} xl={4} /> | ||
|
||
<Grid tablet={8} desktop={6} xl={4}> | ||
<Card title={CARD_TITLE_BY_PATH[v2PathPrefix(pagePath)]}> | ||
<div className='flex flex-col gap-4'> | ||
<Tabs | ||
value={v2PathPrefix(pagePath)} | ||
onChange={value => navigate(value)} | ||
options={TABS_OPTIONS} | ||
actionType='accent' | ||
/> | ||
|
||
<Outlet /> | ||
</div> | ||
</Card> | ||
</Grid> | ||
|
||
<Grid mobile={0} tablet={2} desktop={3} xl={4} /> | ||
</Grid> | ||
); | ||
}; |
23 changes: 23 additions & 0 deletions
23
apps/minifront/src/components/v2/dashboard-layout/transactions-card-title.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,23 @@ | ||
import { Button } from '@repo/ui/Button'; | ||
import { Dialog } from '@repo/ui/Dialog'; | ||
import { Text } from '@repo/ui/Text'; | ||
import { Info } from 'lucide-react'; | ||
|
||
export const TransactionsCardTitle = () => ( | ||
<div className='flex items-center gap-2'> | ||
Transactions List | ||
<Dialog> | ||
<Dialog.Trigger asChild> | ||
<Button icon={Info} iconOnly='adornment'> | ||
Info | ||
</Button> | ||
</Dialog.Trigger> | ||
<Dialog.Content title='Transactions List'> | ||
<Text> | ||
Your wallet scans shielded chain data locally and indexes all relevant transactions it | ||
detects, both incoming and outgoing. | ||
</Text> | ||
</Dialog.Content> | ||
</Dialog> | ||
</div> | ||
); |
52 changes: 52 additions & 0 deletions
52
apps/minifront/src/components/v2/dashboard-layout/transactions-page/index.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,52 @@ | ||
import { Table } from '@repo/ui/Table'; | ||
import { useSummaries } from '../../../../state/transactions'; | ||
import { Text } from '@repo/ui/Text'; | ||
import { Link } from 'react-router-dom'; | ||
import { SquareArrowOutUpRight } from 'lucide-react'; | ||
import { Button } from '@repo/ui/Button'; | ||
|
||
export const TransactionsPage = () => { | ||
const summaries = useSummaries(); | ||
|
||
return ( | ||
<Table layout='fixed'> | ||
<Table.Thead> | ||
<Table.Tr> | ||
<Table.Th width='150px'>Block Height</Table.Th> | ||
<Table.Th>Description</Table.Th> | ||
<Table.Th width='125px'>Hash</Table.Th> | ||
</Table.Tr> | ||
</Table.Thead> | ||
<Table.Tbody> | ||
{summaries.data?.map(summary => ( | ||
<Table.Tr key={summary.hash}> | ||
<Table.Td> | ||
<Text>{summary.height}</Text> | ||
</Table.Td> | ||
<Table.Td> | ||
<Text>{summary.description}</Text> | ||
</Table.Td> | ||
<Table.Td> | ||
<div className='flex gap-1'> | ||
<Link | ||
to={`/tx/${summary.hash}`} | ||
className='shrink overflow-hidden' | ||
title={summary.hash} | ||
> | ||
<Text truncate as='div'> | ||
{summary.hash} | ||
</Text> | ||
</Link> | ||
<Link to={`/tx/${summary.hash}`}> | ||
<Button icon={SquareArrowOutUpRight} iconOnly='adornment'> | ||
View transaction | ||
</Button> | ||
</Link> | ||
</div> | ||
</Table.Td> | ||
</Table.Tr> | ||
))} | ||
</Table.Tbody> | ||
</Table> | ||
); | ||
}; |
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,14 @@ | ||
import { Display } from '@repo/ui/Display'; | ||
import { HeadTag } from '../metadata/head-tag'; | ||
import { Outlet } from 'react-router-dom'; | ||
import { Toaster } from '@repo/ui/components/ui/toaster'; | ||
import { SyncingDialog } from '../syncing-dialog'; | ||
|
||
export const Layout = () => ( | ||
<Display> | ||
<HeadTag /> | ||
<Outlet /> | ||
<Toaster /> | ||
<SyncingDialog /> | ||
</Display> | ||
); |
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,52 @@ | ||
import { redirect, RouteObject } from 'react-router-dom'; | ||
import { Layout } from './layout'; | ||
import { abortLoader } from '../../abort-loader'; | ||
import { PagePath } from '../metadata/paths'; | ||
import { DashboardLayout } from './dashboard-layout'; | ||
import { AssetsPage } from './dashboard-layout/assets-page'; | ||
import { TransactionsPage } from './dashboard-layout/transactions-page'; | ||
|
||
/** @todo: Delete this helper once we switch over to the v2 layout. */ | ||
const temporarilyPrefixPathsWithV2 = (routes: RouteObject[]): RouteObject[] => | ||
routes.map(route => { | ||
if (route.index) { | ||
return route; | ||
} | ||
|
||
return { | ||
...route, | ||
path: `/v2${route.path === '/' ? '' : route.path}`, | ||
...(route.children ? { children: temporarilyPrefixPathsWithV2(route.children) } : {}), | ||
}; | ||
}); | ||
|
||
/** | ||
* @todo: Once we switch over to the v2 layout, we need to: | ||
* 1) pass these routes to `createHashRouter()` and export the returned router, | ||
* like in `../root-router.tsx`. | ||
* 2) remove the call to `temporarilyPrefixPathsWithV2()`. | ||
*/ | ||
export const routes: RouteObject[] = temporarilyPrefixPathsWithV2([ | ||
{ | ||
path: '/', | ||
element: <Layout />, | ||
loader: abortLoader, | ||
children: [ | ||
{ index: true, loader: () => redirect(`/v2${PagePath.DASHBOARD}`) }, | ||
{ | ||
path: PagePath.DASHBOARD, | ||
element: <DashboardLayout />, | ||
children: [ | ||
{ | ||
index: true, | ||
element: <AssetsPage />, | ||
}, | ||
{ | ||
path: PagePath.TRANSACTIONS, | ||
element: <TransactionsPage />, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
]); |
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
Oops, something went wrong.