Skip to content

Commit

Permalink
Create v2 send/receive pages and corresponding components
Browse files Browse the repository at this point in the history
  • Loading branch information
jessepinho committed Aug 17, 2024
1 parent 9964151 commit c7ef90e
Show file tree
Hide file tree
Showing 54 changed files with 1,746 additions and 261 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-vans-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/getters': major
---

Update asset ID getter; it no longer has a hardcoded Optional version
2 changes: 1 addition & 1 deletion apps/minifront/src/components/send/send-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { InputBlock } from '../../shared/input-block';
import { useMemo } from 'react';
import { penumbraAddrValidation } from '../helpers';
import InputToken from '../../shared/input-token';
import { useRefreshFee } from './use-refresh-fee';
import { GasFee } from '../../shared/gas-fee';
import { useBalancesResponses, useStakingTokenMetadata } from '../../../state/shared';
import { NonNativeFeeWarning } from '../../shared/non-native-fee-warning';
import { transferableBalancesResponsesSelector } from '../../../state/send/helpers';
import { useRefreshFee } from '../../v2/transfer-layout/send-page/use-refresh-fee';

export const SendForm = () => {
const stakingTokenMetadata = useStakingTokenMetadata();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ReactNode, useCallback, useEffect, useState } from 'react';
import {
getAddressIndex,
getAmount,
getAssetIdFromBalancesResponseOptional,
getAssetIdFromBalancesResponse,
} from '@penumbra-zone/getters/balances-response';
import { ViewService } from '@penumbra-zone/protobuf';
import { GasPrices } from '@penumbra-zone/protobuf/penumbra/core/component/fee/v1/fee_pb';
Expand Down Expand Up @@ -54,7 +54,7 @@ const hasTokenBalance = ({
}

return gasPrices.some(price =>
price.assetId?.equals(getAssetIdFromBalancesResponseOptional(balance)),
price.assetId?.equals(getAssetIdFromBalancesResponse.optional()(balance)),
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,27 @@ import { CharacterTransition } from '@repo/ui/CharacterTransition';
import { Dialog } from '@repo/ui/Dialog';
import { Text } from '@repo/ui/Text';
import { Info } from 'lucide-react';
import { useId } from 'react';

export const AssetsCardTitle = () => (
<div className='flex items-center gap-2'>
<CharacterTransition>Asset Balances</CharacterTransition>
<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>
);
export const AssetsCardTitle = () => {
const layoutId = useId();
return (
<div className='flex items-center gap-2'>
<CharacterTransition>Asset Balances</CharacterTransition>
<Dialog>
<Dialog.Trigger asChild>
<Button icon={Info} iconOnly='adornment' motion={{ layoutId }}>
Info
</Button>
</Dialog.Trigger>
<Dialog.Content title='Asset Balances' motion={{ layoutId }}>
<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>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { TableTitle } from './table-title';
import { Link } from 'react-router-dom';
import { Button } from '@repo/ui/Button';
import { ArrowRightLeft } from 'lucide-react';
import { useAnimationDeferredValue } from '@repo/ui/hooks/useAnimationDeferredValue';
import { ConditionalWrap } from '@repo/ui/ConditionalWrap';
import { LayoutGroup } from 'framer-motion';

const getTradeLink = (balance: BalancesResponse): string => {
const metadata = getMetadataFromBalancesResponseOptional(balance);
Expand All @@ -36,46 +39,69 @@ export const AssetsPage = () => {
shouldReselect: (before, after) => before?.data !== after.data,
});

return (
<>
{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>
const deferredBalancesByAccount = useAnimationDeferredValue(balancesByAccount);

return deferredBalancesByAccount?.map((account, index) => (
<ConditionalWrap
key={account.account}
// Only wrap the first table in the `<LayoutGroup />`, since that's the
// only one that will be animating when transitioning to the assets table.
if={index === 0}
then={children => <LayoutGroup id='dashboardContent'>{children}</LayoutGroup>}
>
<Table
tableLayout='fixed'
title={<TableTitle account={account} />}
motion={{ layoutId: index === 0 ? 'table' : undefined }}
>
<Table.Thead>
<Table.Tr>
<Table.Th
width={`calc(50% - (${BUTTON_CELL_WIDTH_PX} / 2))`}
motion={{ layoutId: index === 0 ? 'th1' : undefined }}
>
Asset
</Table.Th>
<Table.Th
width={`calc(50% - (${BUTTON_CELL_WIDTH_PX} / 2))`}
motion={{ layoutId: index === 0 ? 'th2' : undefined }}
>
Estimate
</Table.Th>
<Table.Th
width={BUTTON_CELL_WIDTH_PX}
motion={{ layoutId: index === 0 ? 'th3' : undefined }}
/>
</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>
<div className='h-8 w-10 overflow-hidden'>
<Link
to={getTradeLink(balance)}
className='block translate-x-full opacity-0 transition [tr:hover>td>div>&]:translate-x-0 [tr:hover>td>div>&]:opacity-100'
>
<Density compact>
<Button icon={ArrowRightLeft} iconOnly>
Trade
</Button>
</Density>
</Link>
</div>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
))}
</>
);
<Table.Td>
<div className='h-8 w-10 overflow-hidden'>
<Link
to={getTradeLink(balance)}
className='block translate-x-full opacity-0 transition [tr:hover>td>div>&]:translate-x-0 [tr:hover>td>div>&]:opacity-100'
>
<Density compact>
<Button icon={ArrowRightLeft} iconOnly>
Trade
</Button>
</Density>
</Link>
</div>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</ConditionalWrap>
));
};
33 changes: 16 additions & 17 deletions apps/minifront/src/components/v2/dashboard-layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { usePagePath } from '../../../fetchers/page-path';
import { PagePath } from '../../metadata/paths';
import { AssetsCardTitle } from './assets-card-title';
import { TransactionsCardTitle } from './transactions-card-title';
import { LayoutGroup, motion } from 'framer-motion';
import { motion } from 'framer-motion';

/** @todo: Remove this function and its uses after we switch to v2 layout */
const v2PathPrefix = (path: string) => `/v2${path}`;
Expand All @@ -30,22 +30,21 @@ export const DashboardLayout = () => {
<Grid mobile={0} tablet={2} desktop={3} xl={4} />

<Grid tablet={8} desktop={6} xl={4}>
<LayoutGroup id='dashboard'>
<Card title={CARD_TITLE_BY_PATH[v2PathPrefix(pagePath)]} layout layoutId='main'>
<motion.div layout>
<Tabs
value={v2PathPrefix(pagePath)}
onChange={value => navigate(value)}
options={TABS_OPTIONS}
actionType='accent'
/>
</motion.div>

<LayoutGroup id='dashboardContent'>
<Outlet />
</LayoutGroup>
</Card>
</LayoutGroup>
<Card
title={CARD_TITLE_BY_PATH[v2PathPrefix(pagePath)]}
motion={{ layout: true, layoutId: 'main' }}
>
<motion.div layout>
<Tabs
value={v2PathPrefix(pagePath)}
onChange={value => navigate(value)}
options={TABS_OPTIONS}
actionType='accent'
/>
</motion.div>

<Outlet />
</Card>
</Grid>

<Grid mobile={0} tablet={2} desktop={3} xl={4} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@ import { CharacterTransition } from '@repo/ui/CharacterTransition';
import { Dialog } from '@repo/ui/Dialog';
import { Text } from '@repo/ui/Text';
import { Info } from 'lucide-react';
import { useId } from 'react';

export const TransactionsCardTitle = () => (
<div className='flex items-center gap-2'>
<CharacterTransition>Transactions List</CharacterTransition>
<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>
);
export const TransactionsCardTitle = () => {
const layoutId = useId();
return (
<div className='flex items-center gap-2'>
<CharacterTransition>Transactions List</CharacterTransition>
<Dialog>
<Dialog.Trigger asChild>
<Button icon={Info} iconOnly='adornment' motion={{ layoutId }}>
Info
</Button>
</Dialog.Trigger>
<Dialog.Content title='Transactions List' motion={{ layoutId }}>
<Text>
Your wallet scans shielded chain data locally and indexes all relevant transactions it
detects, both incoming and outgoing.
</Text>
</Dialog.Content>
</Dialog>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,58 @@ import { Text } from '@repo/ui/Text';
import { Link } from 'react-router-dom';
import { SquareArrowOutUpRight } from 'lucide-react';
import { Button } from '@repo/ui/Button';
import { useAnimationDeferredValue } from '@repo/ui/hooks/useAnimationDeferredValue';
import { LayoutGroup } from 'framer-motion';

export const TransactionsPage = () => {
const summaries = useSummaries();
const deferredSummariesData = useAnimationDeferredValue(summaries.data);

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>
<LayoutGroup id='dashboardContent'>
<Table tableLayout='fixed' motion={{ layoutId: 'table' }}>
<Table.Thead>
<Table.Tr>
<Table.Th width='150px' motion={{ layoutId: 'th1' }}>
Block Height
</Table.Th>
<Table.Th motion={{ layoutId: 'th2' }}>Description</Table.Th>
<Table.Th width='125px' motion={{ layoutId: 'th3' }}>
Hash
</Table.Th>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.Thead>
<Table.Tbody>
{deferredSummariesData?.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>
</LayoutGroup>
);
};
Loading

0 comments on commit c7ef90e

Please sign in to comment.