Skip to content
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

Create initial pass of v2 send/receive pages #1665

Merged
merged 7 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I deleted the use-refresh-fee.ts file from the v1 directory, and moved it to the v2 directory. Now, we're "reaching into" the v2 directory from v1 to import its logic.

I think this is a good approach for logic-heavy files (like hooks, etc.), since eventually, we'll want to just delete all the v1 code and move the v2 code up into src. At that point, if we've already moved all the v1 hooks/etc. into the v2 directory, the migration will be a cinch.

And in the meantime, we don't need to maintain two copies of each hook file (i.e., we don't have to have a copy of use-refresh-fee.ts in both the v1 src directory, and the v2 directory).


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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got rid of the optional one, since we can now just call .optional() on it.

} 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 = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to review this file with whitespace changes hidden. I just added a useId() call, and then passed it to the dialog trigger and content to make the dialog "grow out of" the info icon.

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 = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to review this file with whitespace changes hidden. I just added a useId() call, and then passed it to the dialog trigger and content to make the dialog "grow out of" the info icon.

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
Loading