Skip to content

Commit

Permalink
feat: #65: pair selector (#85)
Browse files Browse the repository at this point in the history
* feat: #65: implement pair selector

* feat: #65: fill pair selector with account balances

* feat: fill the `PairSelector` with the registry values

* feat: #65: use react-query to fetch assets and balances

* refactor: #65: use setters in the stores

* fix: #65: use react-query instead of mobx to keep the state of the fetchers

* fix: #65: apply latest changes

* fix: #65: apply PR comments and add better docs

* fix: #65: rename balances queryKey
  • Loading branch information
VanishMax authored Oct 10, 2024
1 parent b19776d commit bc72b87
Show file tree
Hide file tree
Showing 14 changed files with 329 additions and 99 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-debug.log*
yarn-error.log*

# local env files
.env
.env.local
.env.development.local
.env.test.local
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
"@grpc/proto-loader": "^0.7.13",
"@penumbra-labs/registry": "^11.3.1",
"@penumbra-zone/client": "^19.0.0",
"@penumbra-zone/getters": "^18.0.0",
"@penumbra-zone/protobuf": "^6.1.0",
"@penumbra-zone/transport-dom": "^7.5.0",
"@penumbra-zone/ui": "^10.0.0",
"@penumbra-zone/types": "^24.0.0",
"@penumbra-zone/ui": "^10.0.2",
"@penumbra-zone/wasm": "^26.2.0",
"@radix-ui/react-icons": "^1.3.0",
"@rehooks/component-size": "^1.0.3",
Expand Down Expand Up @@ -70,7 +72,6 @@
"zod": "^3.23.8"
},
"devDependencies": {
"styled-components": "^6.1.13",
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
Expand All @@ -92,6 +93,7 @@
"eslint-plugin-react": "^7.35.2",
"globals": "^15.9.0",
"postcss": "^8.4.47",
"styled-components": "^6.1.13",
"tailwindcss": "^3.4.12",
"ts-loader": "^9.5.1",
"typescript": "^5.5.4",
Expand Down
207 changes: 117 additions & 90 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions src/app/Providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client';

import { ReactNode } from 'react';
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '@/shared/queryClient';
import { StyledComponentsRegistry } from './StyleRegistry';

export const Providers = ({ children }: { children: ReactNode }) => {
return (
<StyledComponentsRegistry>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</StyledComponentsRegistry>
);
};
6 changes: 3 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { ReactNode } from 'react';
import '@/v2.css';
import { StyledComponentsRegistry } from './StyleRegistry';
import { Providers } from './Providers';

const RootLayout = ({ children }: { children: ReactNode }) => {
return (
<html lang="en">
<body>
<StyledComponentsRegistry>
<Providers>
{children}
</StyledComponentsRegistry>
</Providers>
</body>
</html>
)
Expand Down
18 changes: 16 additions & 2 deletions src/app/v2/trade/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
'use client';

import { Card } from '@penumbra-zone/ui/Card';
import { PairSelector } from '@/components/PairSelector';
import { observer } from 'mobx-react-lite';
import { pairStore } from '@/shared/state/pair';

const TradePage = observer(() => {
const { from, setFrom, to, setTo } = pairStore;

const TradePage = () => {
return (
<div>
<div className="flex gap-2">
<PairSelector
to={to}
onToChange={setTo}
from={from}
onFromChange={setFrom}
/>
</div>

<div className='flex flex-wrap lg:gap-2'>
<div className='w-full lg:w-auto lg:flex-grow mb-2'>
<Card title='Chart'>
Expand Down Expand Up @@ -41,6 +55,6 @@ const TradePage = () => {
</div>
</div>
);
};
});

export default TradePage;
68 changes: 68 additions & 0 deletions src/components/PairSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { observer } from 'mobx-react-lite';
import { ArrowLeftRight } from 'lucide-react';
import { AssetSelector, AssetSelectorValue } from '@penumbra-zone/ui/AssetSelector';
import { Button } from '@penumbra-zone/ui/Button';
import { useAssets } from '@/shared/state/assets';
import { useBalances } from '@/shared/state/balances';

export interface PairSelectorProps {
/** The `Metadata` or `BalancesResponse`, from which the swap should be initiated */
from?: AssetSelectorValue;
onFromChange?: (value?: AssetSelectorValue) => void;

/** The `Metadata` or `BalancesResponse`, to which the swap should be made */
to?: AssetSelectorValue;
onToChange?: (value?: AssetSelectorValue) => void;

dialogTitle?: string;
disabled?: boolean;
}

export const PairSelector = observer(({
from,
onFromChange,
to,
onToChange,
disabled,
dialogTitle,
}: PairSelectorProps) => {
const { data: assets } = useAssets();
const { data: balances } = useBalances();

const onSwap = () => {
onFromChange?.(to);
onToChange?.(from);
};

return (
<div className="flex gap-2">
<AssetSelector
value={from}
assets={assets}
balances={balances}
disabled={disabled}
dialogTitle={dialogTitle}
onChange={onFromChange}
/>

<Button
priority='primary'
iconOnly
icon={ArrowLeftRight}
disabled={disabled}
onClick={onSwap}
>
Swap
</Button>

<AssetSelector
value={to}
assets={assets}
balances={balances}
disabled={disabled}
dialogTitle={dialogTitle}
onChange={onToChange}
/>
</div>
);
});
2 changes: 1 addition & 1 deletion src/old/utils/token/tokenFetch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ export const decimalsFromDenomUnits = (denomUnits: DenomUnit[]): number => {
}
});
return decimals;
};
};
2 changes: 1 addition & 1 deletion src/pages/block/[block_height].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -706,4 +706,4 @@ export default function Block() {
)}
</Layout>
);
}
}
3 changes: 3 additions & 0 deletions src/shared/queryClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { QueryClient } from '@tanstack/react-query'

export const queryClient = new QueryClient();
41 changes: 41 additions & 0 deletions src/shared/state/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ViewService } from '@penumbra-zone/protobuf';
import { getDenomMetadata } from '@penumbra-zone/getters/assets-response';
import { penumbra } from '@/shared/penumbra';
import { connectionStore } from '@/shared/state/connection';
import { useQuery } from '@tanstack/react-query';
import { useRegistry } from '@/fetchers/registry';

/**
* Returns the `Metadata[]` based on the provider connection state.
* If connected, it fetches the assets from the `ViewService`'s `assets` method.
* Otherwise, it fetches the assets from the remote registry.
*
* Must be used within `observer` mobX HOC
**/
export const useAssets = () => {
const { data: registry, error: registryErr } = useRegistry();

const registryAssets = useQuery({
queryKey: ['registry-assets'],
enabled: Boolean(registry),
queryFn: () => {
if (!registry) {
throw new Error('Registry not available');
}
return registry.getAllAssets();
},
});

const accountAssets = useQuery({
queryKey: ['view-service-assets'],
enabled: connectionStore.connected,
queryFn: async () => {
const responses = await Array.fromAsync(penumbra.service(ViewService).assets({}));
return responses.map(getDenomMetadata);
},
});

return connectionStore.connected
? accountAssets
: { ...registryAssets, error: registryAssets.error ?? registryErr };
};
21 changes: 21 additions & 0 deletions src/shared/state/balances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ViewService } from '@penumbra-zone/protobuf';
import { BalancesResponse } from '@penumbra-zone/protobuf/penumbra/view/v1/view_pb';
import { penumbra } from '@/shared/penumbra';
import { connectionStore } from '@/shared/state/connection';
import { useQuery } from '@tanstack/react-query';

const fetchQuery = async (): Promise<BalancesResponse[]> => {
return Array.fromAsync(penumbra.service(ViewService).balances({}));
};

/**
* Fetches the `BalancesResponse[]` based on the provider connection state.
* Must be used within the `observer` mobX HOC
*/
export const useBalances = () => {
return useQuery({
queryKey: ['view-service-balances'],
queryFn: fetchQuery,
enabled: connectionStore.connected,
});
};
21 changes: 21 additions & 0 deletions src/shared/state/pair.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { makeAutoObservable } from 'mobx';
import { AssetSelectorValue } from '@penumbra-zone/ui/AssetSelector';

class PairState {
from?: AssetSelectorValue;
to?: AssetSelectorValue;

constructor () {
makeAutoObservable(this);
}

setFrom = (value?: AssetSelectorValue) => {
this.from = value;
}

setTo = (value?: AssetSelectorValue) => {
this.to = value;
}
}

export const pairStore = new PairState();
16 changes: 16 additions & 0 deletions styles/v2.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,19 @@ body > section {
position: relative;
z-index: 0;
}

::-webkit-scrollbar {
width: 4px;
}

::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.25);
}

::-webkit-scrollbar-thumb {
background: #888;
}

::-webkit-scrollbar-thumb:hover {
background: #555;
}

0 comments on commit bc72b87

Please sign in to comment.