Skip to content

Commit

Permalink
Stop using loader functions, part 1 (#1306)
Browse files Browse the repository at this point in the history
* Fix typing of getBalances

* Fix TypeScript complaint

* Allow abortLoader to be used in root router

* Use ZQuery for balances

* Update transactions slice to use ZQuery

* Use ZQuery for the Send form

* Separate out IBC in slice files for weird import reasons

* Fix little TypeScript issue

* Convert transaction details to ZQuery

* Move staking loader data to ZQuery

* Fix typings issues

* Remove unnecessary assignment

* Changeset

* Remove unused imports

* Document AllSlices

* Remove unused import
  • Loading branch information
jessepinho authored Jun 19, 2024
1 parent 219c6fb commit 733d62a
Show file tree
Hide file tree
Showing 36 changed files with 442 additions and 321 deletions.
6 changes: 6 additions & 0 deletions .changeset/beige-fans-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@penumbra-zone/zquery': patch
'minifront': patch
---

Stop using loader functions for most routes; fix typings issue in ZQuery
1 change: 1 addition & 0 deletions apps/minifront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@radix-ui/react-menubar": "^1.0.4",
"@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-portal": "^1.0.4",
"@remix-run/router": "^1.16.1",
"@repo/ui": "workspace:*",
"@tanstack/react-query": "4.36.1",
"bech32": "^2.0.0",
Expand Down
6 changes: 5 additions & 1 deletion apps/minifront/src/abort-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ const retry = async (fn: () => boolean, ms = 500, rate = Math.max(ms / 10, 50))
* timeout. This is a temporary solution until loaders properly await Prax
* connection.
*/
export const abortLoader = async (): Promise<void> => {
export const abortLoader = async (): Promise<null> => {
await throwIfPraxNotInstalled();
await retry(() => isPraxConnected());
throwIfPraxNotConnected();

// Loaders are required to return a value, even if it's null. By returning
// `null` here, we can use this loader directly in the router.
return null;
};
15 changes: 4 additions & 11 deletions apps/minifront/src/components/dashboard/assets-table/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { LoaderFunction, useLoaderData } from 'react-router-dom';
import { AddressIcon } from '@repo/ui/components/ui/address-icon';
import { AddressComponent } from '@repo/ui/components/ui/address-component';
import { BalancesByAccount, getBalancesByAccount } from '../../../fetchers/balances/by-account';
import {
Table,
TableBody,
Expand All @@ -11,20 +9,15 @@ import {
TableRow,
} from '@repo/ui/components/ui/table';
import { ValueViewComponent } from '@repo/ui/components/ui/tx/view/value';
import { abortLoader } from '../../../abort-loader';
import { EquivalentValues } from './equivalent-values';
import { Fragment } from 'react';
import { shouldDisplay } from './helpers';

export const AssetsLoader: LoaderFunction = async (): Promise<BalancesByAccount[]> => {
await abortLoader();
return await getBalancesByAccount();
};
import { useBalancesByAccount } from '../../../state/balances';

export default function AssetsTable() {
const balancesByAccount = useLoaderData() as BalancesByAccount[];
const balancesByAccount = useBalancesByAccount();

if (balancesByAccount.length === 0) {
if (balancesByAccount.data?.length === 0) {
return (
<div className='flex flex-col gap-6'>
<p>
Expand All @@ -40,7 +33,7 @@ export default function AssetsTable() {

return (
<Table>
{balancesByAccount.map(account => (
{balancesByAccount.data?.map(account => (
<Fragment key={account.account}>
<TableHeader className='group'>
<TableRow>
Expand Down
13 changes: 4 additions & 9 deletions apps/minifront/src/components/dashboard/transaction-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@ import {
} from '@repo/ui/components/ui/table';
import { Link } from 'react-router-dom';
import { shorten } from '@penumbra-zone/types/string';
import { useStore } from '../../state';
import { memo, useEffect } from 'react';
import { TransactionSummary } from '../../state/transactions';
import { memo } from 'react';
import { TransactionSummary, useSummaries } from '../../state/transactions';

export default function TransactionTable() {
const { summaries, loadSummaries } = useStore(store => store.transactions);

useEffect(() => void loadSummaries(), [loadSummaries]);
const summaries = useSummaries();

return (
<Table className='md:table'>
Expand All @@ -28,9 +25,7 @@ export default function TransactionTable() {
</TableRow>
</TableHeader>
<TableBody>
{summaries.map(summary => (
<Row key={summary.hash} summary={summary} />
))}
{summaries.data?.map(summary => <Row key={summary.hash} summary={summary} />)}
</TableBody>
</Table>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/minifront/src/components/ibc/ibc-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const IbcLoader: LoaderFunction = async (): Promise<IbcLoaderResponse> =>
const initialSelection = filterBalancesPerChain(
assetBalances,
initialChain,
stakingTokenMetadata,
assets,
stakingTokenMetadata,
)[0];

// set initial account if accounts exist and asset if account has asset list
Expand Down
11 changes: 9 additions & 2 deletions apps/minifront/src/components/ibc/ibc-out/ibc-out-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import InputToken from '../../shared/input-token';
import { InputBlock } from '../../shared/input-block';
import { IbcLoaderResponse } from '../ibc-loader';
import { LockOpen2Icon } from '@radix-ui/react-icons';
import { useStakingTokenMetadata } from '../../../state/shared';

export const IbcOutForm = () => {
const { balances, stakingTokenMetadata, assets } = useLoaderData() as IbcLoaderResponse;
const stakingTokenMetadata = useStakingTokenMetadata();
const { balances, assets } = useLoaderData() as IbcLoaderResponse;
const {
sendIbcWithdraw,
destinationChainAddress,
Expand All @@ -25,7 +27,12 @@ export const IbcOutForm = () => {
setSelection,
chain,
} = useStore(ibcOutSelector);
const filteredBalances = filterBalancesPerChain(balances, chain, stakingTokenMetadata, assets);
const filteredBalances = filterBalancesPerChain(
balances,
chain,
assets,
stakingTokenMetadata.data,
);
const validationErrors = useStore(ibcValidationErrors);

return (
Expand Down
20 changes: 11 additions & 9 deletions apps/minifront/src/components/root-router.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { createHashRouter, redirect } from 'react-router-dom';
import { PagePath } from './metadata/paths';
import { Layout } from './layout';
import AssetsTable, { AssetsLoader } from './dashboard/assets-table';
import AssetsTable from './dashboard/assets-table';
import TransactionTable from './dashboard/transaction-table';
import { DashboardLayout } from './dashboard/layout';
import { TxDetails, TxDetailsErrorBoundary, TxDetailsLoader } from './tx-details';
import { TxDetails, TxDetailsErrorBoundary } from './tx-details';
import { SendLayout } from './send/layout';
import { SendAssetBalanceLoader, SendForm } from './send/send-form';
import { SendForm } from './send/send-form';
import { Receive } from './send/receive';
import { ErrorBoundary } from './shared/error-boundary';
import { SwapLayout } from './swap/layout';
import { SwapLoader } from './swap/swap-loader';
import { StakingLayout, StakingLoader } from './staking/layout';
import { StakingLayout } from './staking/layout';
import { IbcLoader } from './ibc/ibc-loader';
import { IbcLayout } from './ibc/layout';
import { abortLoader } from '../abort-loader';
import type { Router } from '@remix-run/router';

export const rootRouter = createHashRouter([
export const rootRouter: Router = createHashRouter([
{
path: '/',
element: <Layout />,
Expand All @@ -28,7 +30,7 @@ export const rootRouter = createHashRouter([
children: [
{
index: true,
loader: AssetsLoader,
loader: abortLoader,
element: <AssetsTable />,
},
{
Expand All @@ -43,7 +45,7 @@ export const rootRouter = createHashRouter([
children: [
{
index: true,
loader: SendAssetBalanceLoader,
loader: abortLoader,
element: <SendForm />,
},
{
Expand All @@ -59,13 +61,13 @@ export const rootRouter = createHashRouter([
},
{
path: PagePath.TRANSACTION_DETAILS,
loader: TxDetailsLoader,
loader: abortLoader,
element: <TxDetails />,
errorElement: <TxDetailsErrorBoundary />,
},
{
path: PagePath.STAKING,
loader: StakingLoader,
loader: abortLoader,
element: <StakingLayout />,
},
{
Expand Down
46 changes: 15 additions & 31 deletions apps/minifront/src/components/send/send-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,23 @@
import { Button } from '@repo/ui/components/ui/button';
import { Input } from '@repo/ui/components/ui/input';
import { useStore } from '../../../state';
import { sendSelector, sendValidationErrors } from '../../../state/send';
import {
sendSelector,
sendValidationErrors,
useTransferableBalancesResponses,
} from '../../../state/send';
import { InputBlock } from '../../shared/input-block';
import { LoaderFunction, useLoaderData } from 'react-router-dom';
import { useMemo, useState } from 'react';
import { getTransferableBalancesResponses, penumbraAddrValidation } from '../helpers';
import { abortLoader } from '../../../abort-loader';
import { penumbraAddrValidation } from '../helpers';
import InputToken from '../../shared/input-token';
import { useRefreshFee } from './use-refresh-fee';
import { GasFee } from '../../shared/gas-fee';
import { BalancesResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb';
import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb';
import { getStakingTokenMetadata } from '../../../fetchers/registry';
import { hasStakingToken } from '../../../fetchers/staking-token';

export interface SendLoaderResponse {
assetBalances: BalancesResponse[];
stakingAssetMetadata: Metadata;
}

export const SendAssetBalanceLoader: LoaderFunction = async (): Promise<SendLoaderResponse> => {
await abortLoader();
const assetBalances = await getTransferableBalancesResponses();

if (assetBalances[0]) {
// set initial account if accounts exist and asset if account has asset list
useStore.setState(state => {
state.send.selection = assetBalances[0];
});
}
const stakingAssetMetadata = await getStakingTokenMetadata();

return { assetBalances, stakingAssetMetadata };
};
import { useStakingTokenMetadata } from '../../../state/shared';

export const SendForm = () => {
const { assetBalances, stakingAssetMetadata } = useLoaderData() as SendLoaderResponse;
const stakingTokenMetadata = useStakingTokenMetadata();
const transferableBalancesResponses = useTransferableBalancesResponses();
const {
selection,
amount,
Expand All @@ -56,7 +37,10 @@ export const SendForm = () => {
const [showNonNativeFeeWarning, setshowNonNativeFeeWarning] = useState(false);

// Check if the user has native staking tokens
const stakingToken = hasStakingToken(assetBalances, stakingAssetMetadata);
const stakingToken = hasStakingToken(
transferableBalancesResponses.data,
stakingTokenMetadata.data,
);

useRefreshFee();

Expand Down Expand Up @@ -106,7 +90,7 @@ export const SendForm = () => {
checkFn: () => validationErrors.amountErr,
},
]}
balances={assetBalances}
balances={transferableBalancesResponses.data ?? []}
/>
{showNonNativeFeeWarning && (
<div className='rounded border border-yellow-500 bg-gray-800 p-4 text-yellow-500'>
Expand All @@ -121,7 +105,7 @@ export const SendForm = () => {
<GasFee
fee={fee}
feeTier={feeTier}
stakingAssetMetadata={stakingAssetMetadata}
stakingAssetMetadata={stakingTokenMetadata.data}
setFeeTier={setFeeTier}
/>

Expand Down
4 changes: 3 additions & 1 deletion apps/minifront/src/components/shared/gas-fee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ export const GasFee = ({
}: {
fee: Fee | undefined;
feeTier: FeeTier_Tier;
stakingAssetMetadata: Metadata;
stakingAssetMetadata?: Metadata;
setFeeTier: (feeTier: FeeTier_Tier) => void;
}) => {
if (!stakingAssetMetadata) return null;

let feeValueView: ValueView | undefined;
if (fee?.amount)
feeValueView = new ValueView({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { DelegationValueView } from '.';
import { render } from '@testing-library/react';
import {
Expand Down Expand Up @@ -47,7 +47,7 @@ const STAKING_TOKEN_METADATA = new Metadata({

const validatorInfo = new ValidatorInfo({
validator: {
identityKey: {},
identityKey: validatorIk,
fundingStreams: [
{
recipient: {
Expand Down Expand Up @@ -98,27 +98,32 @@ const valueView = new ValueView({
},
});

const mockUseStakingTokenMetadata = vi.hoisted(() => () => ({
data: STAKING_TOKEN_METADATA,
error: undefined,
loading: undefined,
}));

vi.mock('../../../../state/shared', async () => ({
...(await vi.importActual('../../../../state/shared')),
useStakingTokenMetadata: mockUseStakingTokenMetadata,
}));

describe('<DelegationValueView />', () => {
it('shows balance of the delegation token', () => {
const { container } = render(
<DelegationValueView valueView={valueView} stakingTokenMetadata={STAKING_TOKEN_METADATA} />,
);
const { container } = render(<DelegationValueView valueView={valueView} />);

expect(container).toHaveTextContent('1delUM(abc...xyz)');
});

it("shows the delegation token's equivalent value in terms of the staking token", () => {
const { container } = render(
<DelegationValueView valueView={valueView} stakingTokenMetadata={STAKING_TOKEN_METADATA} />,
);
const { container } = render(<DelegationValueView valueView={valueView} />);

expect(container).toHaveTextContent('1.33UM');
});

it('does not show other equivalent values', () => {
const { container } = render(
<DelegationValueView valueView={valueView} stakingTokenMetadata={STAKING_TOKEN_METADATA} />,
);
const { container } = render(<DelegationValueView valueView={valueView} />);

expect(container).not.toHaveTextContent('2.66SOT');
});
Expand Down
Loading

0 comments on commit 733d62a

Please sign in to comment.