Skip to content

Commit

Permalink
feat: #163: pair selector (#204)
Browse files Browse the repository at this point in the history
* fix: apply new UI changes of density

* feat: set up new PairSelector

* feat: add star-pair feature

* feat: implement basic pair selection

* refactor: create more components related to pair selector

* feat: implement filtering input

* feat: implement `/api/pairs` endpoint to fetch the most popular pairs

* feat: implement recent assets in search results

* feat: use balances in the search results

* fix: small bugs

* fix: merge

* fix: ts

* fix: handle the pr feedback
  • Loading branch information
VanishMax authored Dec 19, 2024
1 parent b12be80 commit 8aad7da
Show file tree
Hide file tree
Showing 26 changed files with 1,005 additions and 136 deletions.
1 change: 1 addition & 0 deletions app/api/pairs/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GET } from '@/shared/api/server/summary/pairs';
4 changes: 4 additions & 0 deletions app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { TooltipProvider } from '@penumbra-zone/ui/Tooltip';
import { Header, SyncBar } from '@/widgets/header';
import { queryClient } from '@/shared/const/queryClient';
import { connectionStore } from '@/shared/model/connection';
import { recentPairsStore } from '@/pages/trade/ui/pair-selector/store';
import { starStore } from '@/features/star-pair';

// Used so that observer() won't subscribe to any observables used in an SSR environment
// and no garbage collection problems are introduced.
Expand All @@ -16,6 +18,8 @@ enableStaticRendering(typeof window === 'undefined');
export const App = observer(({ children }: { children: ReactNode }) => {
useEffect(() => {
connectionStore.setup();
recentPairsStore.setup();
starStore.setup();
}, []);

return (
Expand Down
6 changes: 6 additions & 0 deletions global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ declare module '*.css' {
const content: Record<string, string>;
export default content;
}

declare module '*.svg' {
import { FC, SVGAttributes } from 'react';
const value: FC<SVGAttributes<SVGElement>>;
export default value;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@penumbra-zone/protobuf": "^6.3.0",
"@penumbra-zone/transport-dom": "^7.5.0",
"@penumbra-zone/types": "^26.3.0",
"@penumbra-zone/ui": "^13.5.0",
"@penumbra-zone/ui": "^13.6.0",
"@penumbra-zone/wasm": "^34.0.0",
"@radix-ui/react-icons": "^1.3.2",
"@rehooks/component-size": "^1.0.3",
Expand Down
26 changes: 13 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/features/star-pair/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { StarButton } from './star-button';
export { starStore } from './store';
export type { Pair } from './storage';
40 changes: 40 additions & 0 deletions src/features/star-pair/star-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { FC, MouseEventHandler } from 'react';
import { observer } from 'mobx-react-lite';
import { Star } from 'lucide-react';
import { Button } from '@penumbra-zone/ui/Button';
import { Density } from '@penumbra-zone/ui/Density';
import StarFilled from './star-filled.svg';
import type { Pair } from './storage';
import { starStore } from './store';

export interface StarButtonProps {
pair: Pair;
adornment?: boolean;
}

export const StarButton = observer(({ pair, adornment }: StarButtonProps) => {
const { star, unstar, isStarred } = starStore;
const starred = isStarred(pair);

const onClick: MouseEventHandler<HTMLButtonElement> = event => {
event.stopPropagation();
if (starred) {
unstar(pair);
} else {
star(pair);
}
};

return (
<Density compact>
<Button
icon={starred ? (StarFilled as FC) : Star}
priority={adornment ? 'primary' : 'secondary'}
iconOnly={adornment ? 'adornment' : true}
onClick={onClick}
>
Favorite
</Button>
</Density>
);
});
3 changes: 3 additions & 0 deletions src/features/star-pair/star-filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions src/features/star-pair/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Metadata } from '@penumbra-zone/protobuf/penumbra/core/asset/v1/asset_pb';

export interface Pair {
base: Metadata;
quote: Metadata;
}

const STAR_STORE_LS_KEY = 'star-pairs-store';

export const getStarredPairs = (): Pair[] => {
try {
const data = JSON.parse(localStorage.getItem(STAR_STORE_LS_KEY) ?? '[]') as {
base: string;
quote: string;
}[];
return data.map(pair => ({
base: Metadata.fromJson(pair.base),
quote: Metadata.fromJson(pair.quote),
}));
} catch (_) {
return [];
}
};

export const setStarredPairs = (pairs: Pair[]): void => {
localStorage.setItem(
STAR_STORE_LS_KEY,
JSON.stringify(
pairs.map(pair => ({
base: pair.base.toJson(),
quote: pair.quote.toJson(),
})),
),
);
};
34 changes: 34 additions & 0 deletions src/features/star-pair/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { makeAutoObservable } from 'mobx';
import { Pair, getStarredPairs, setStarredPairs } from './storage';

class StarStateStore {
pairs: Pair[] = [];

constructor() {
makeAutoObservable(this);
}

star = (pair: Pair) => {
this.pairs = [...this.pairs, pair];
setStarredPairs(this.pairs);
};

unstar = (pair: Pair) => {
this.pairs = this.pairs.filter(
value => !value.base.equals(pair.base) || !value.quote.equals(pair.quote),
);
setStarredPairs(this.pairs);
};

setup() {
this.pairs = getStarredPairs();
}

isStarred = (pair: Pair): boolean => {
return this.pairs.some(
value => value.base.symbol === pair.base.symbol && value.quote.symbol === pair.quote.symbol,
);
};
}

export const starStore = new StarStateStore();
13 changes: 13 additions & 0 deletions src/pages/trade/api/use-pairs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import type { PairData } from '@/shared/api/server/summary/pairs';
import { apiFetch } from '@/shared/utils/api-fetch';

// Fetches the array of popular (sorted by liquidity) pairs
export const usePairs = () => {
return useQuery({
queryKey: ['pairs'],
queryFn: async () => {
return apiFetch<PairData[]>('/api/pairs');
},
});
};
121 changes: 0 additions & 121 deletions src/pages/trade/ui/pair-selector.tsx

This file was deleted.

Loading

0 comments on commit 8aad7da

Please sign in to comment.