Skip to content

Commit 1776616

Browse files
committed
Optimize payload size for price feeds pages
1 parent c6a2eb9 commit 1776616

File tree

24 files changed

+371
-259
lines changed

24 files changed

+371
-259
lines changed

apps/insights/src/app/layout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { Root as default } from "../components/Root";
22
export { metadata, viewport } from "../metadata";
33

4+
export const dynamic = 'error';
45
export const revalidate = 3600;

apps/insights/src/app/price-feeds/[slug]/layout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export const metadata: Metadata = {
66
title: "Price Feeds",
77
};
88

9+
export const dynamic = 'error';
910
export const revalidate = 3600;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { ChartPage as default } from "../../../components/PriceFeed/chart-page";
22

3+
export const dynamic = 'error';
34
export const revalidate = 3600;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { Publishers as default } from "../../../../components/PriceFeed/publishers";
22

3+
export const dynamic = 'error';
34
export const revalidate = 3600;

apps/insights/src/app/price-feeds/page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export const metadata: Metadata = {
66
title: "Price Feeds",
77
};
88

9+
export const dynamic = 'error';
910
export const revalidate = 3600;

apps/insights/src/app/publishers/[key]/layout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export const metadata: Metadata = {
66
title: "Publishers",
77
};
88

9+
export const dynamic = 'error';
910
export const revalidate = 3600;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { Performance as default } from "../../../components/Publisher/performance";
22

3+
export const dynamic = 'error';
34
export const revalidate = 3600;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { PriceFeeds as default } from "../../../../components/Publisher/price-feeds";
22

3+
export const dynamic = 'error';
34
export const revalidate = 3600;

apps/insights/src/app/publishers/page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export const metadata: Metadata = {
66
title: "Publishers",
77
};
88

9+
export const dynamic = 'error';
910
export const revalidate = 3600;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Badge } from "@pythnetwork/component-library/Badge";
2+
import type { ComponentProps } from "react";
3+
4+
import { usePriceFeeds } from "../../hooks/use-price-feeds";
5+
6+
type Props = ComponentProps<typeof Badge> & {
7+
symbol: string;
8+
};
9+
10+
export const AssetClassTag = ({ symbol }: Props) => {
11+
const feed = usePriceFeeds().get(symbol);
12+
13+
if (feed) {
14+
return (
15+
<Badge variant="neutral" style="outline" size="xs">
16+
{feed.assetClass.toUpperCase()}
17+
</Badge>
18+
);
19+
} else {
20+
throw new NoSuchFeedError(symbol);
21+
}
22+
};
23+
24+
class NoSuchFeedError extends Error {
25+
constructor(symbol: string) {
26+
super(`No feed exists named ${symbol}`);
27+
this.name = "NoSuchFeedError";
28+
}
29+
}

apps/insights/src/components/PriceFeed/layout.tsx

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type { ReactNode } from "react";
1313
import styles from "./layout.module.scss";
1414
import { PriceFeedSelect } from "./price-feed-select";
1515
import { ReferenceData } from "./reference-data";
16-
import { toHex } from "../../hex";
1716
import { Cluster, getFeeds } from "../../services/pyth";
1817
import { FeedKey } from "../FeedKey";
1918
import {
@@ -26,7 +25,6 @@ import {
2625
YesterdaysPricesProvider,
2726
PriceFeedChangePercent,
2827
} from "../PriceFeedChangePercent";
29-
import { PriceFeedIcon } from "../PriceFeedIcon";
3028
import { PriceFeedTag } from "../PriceFeedTag";
3129
import { TabPanel, TabRoot, Tabs } from "../Tabs";
3230

@@ -38,12 +36,12 @@ type Props = {
3836
};
3937

4038
export const PriceFeedLayout = async ({ children, params }: Props) => {
41-
const [{ slug }, fees] = await Promise.all([
39+
const [{ slug }, feeds] = await Promise.all([
4240
params,
4341
getFeeds(Cluster.Pythnet),
4442
]);
4543
const symbol = decodeURIComponent(slug);
46-
const feed = fees.find((item) => item.symbol === symbol);
44+
const feed = feeds.find((item) => item.symbol === symbol);
4745

4846
return feed ? (
4947
<div className={styles.priceFeedLayout}>
@@ -64,22 +62,8 @@ export const PriceFeedLayout = async ({ children, params }: Props) => {
6462
</div>
6563
</div>
6664
<div className={styles.headerRow}>
67-
<PriceFeedSelect
68-
feeds={fees
69-
.filter((feed) => feed.symbol !== symbol)
70-
.map((feed) => ({
71-
id: feed.symbol,
72-
key: toHex(feed.product.price_account),
73-
displaySymbol: feed.product.display_symbol,
74-
icon: <PriceFeedIcon symbol={feed.symbol} />,
75-
assetClass: feed.product.asset_type,
76-
}))}
77-
>
78-
<PriceFeedTag
79-
symbol={feed.product.display_symbol}
80-
description={feed.product.description}
81-
icon={<PriceFeedIcon symbol={feed.symbol} />}
82-
/>
65+
<PriceFeedSelect>
66+
<PriceFeedTag symbol={feed.symbol} />
8367
</PriceFeedSelect>
8468
<div className={styles.rightGroup}>
8569
<FeedKey

apps/insights/src/components/PriceFeed/price-feed-select.tsx

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use client";
22

3-
import { Badge } from "@pythnetwork/component-library/Badge";
43
import { DropdownCaretDown } from "@pythnetwork/component-library/DropdownCaretDown";
54
import {
65
Virtualizer,
@@ -20,39 +19,43 @@ import { type ReactNode, useMemo, useState } from "react";
2019
import { useCollator, useFilter } from "react-aria";
2120

2221
import styles from "./price-feed-select.module.scss";
22+
import { usePriceFeeds } from "../../hooks/use-price-feeds";
23+
import { AssetClassTag } from "../AssetClassTag";
2324
import { PriceFeedTag } from "../PriceFeedTag";
2425

2526
type Props = {
2627
children: ReactNode;
27-
feeds: {
28-
id: string;
29-
key: string;
30-
displaySymbol: string;
31-
icon: ReactNode;
32-
assetClass: string;
33-
}[];
3428
};
3529

36-
export const PriceFeedSelect = ({ children, feeds }: Props) => {
30+
export const PriceFeedSelect = ({ children }: Props) => {
31+
const feeds = usePriceFeeds();
3732
const collator = useCollator();
3833
const filter = useFilter({ sensitivity: "base", usage: "search" });
3934
const [search, setSearch] = useState("");
40-
const sortedFeeds = useMemo(
41-
() =>
42-
feeds.sort((a, b) => collator.compare(a.displaySymbol, b.displaySymbol)),
43-
[feeds, collator],
44-
);
4535
const filteredFeeds = useMemo(
4636
() =>
4737
search === ""
48-
? sortedFeeds
49-
: sortedFeeds.filter(
50-
(feed) =>
51-
filter.contains(feed.displaySymbol, search) ||
52-
filter.contains(feed.assetClass, search) ||
53-
filter.contains(feed.key, search),
54-
),
55-
[sortedFeeds, search, filter],
38+
? feeds.entries()
39+
: feeds
40+
.entries()
41+
.filter(
42+
([, { displaySymbol, assetClass, key }]) =>
43+
filter.contains(displaySymbol, search) ||
44+
filter.contains(assetClass, search) ||
45+
filter.contains(key, search),
46+
),
47+
[feeds, search, filter],
48+
);
49+
const sortedFeeds = useMemo(
50+
() =>
51+
// eslint-disable-next-line unicorn/no-useless-spread
52+
[
53+
...filteredFeeds.map(([symbol, { displaySymbol }]) => ({
54+
id: symbol,
55+
displaySymbol,
56+
})),
57+
].toSorted((a, b) => collator.compare(a.displaySymbol, b.displaySymbol)),
58+
[filteredFeeds, collator],
5659
);
5760
return (
5861
<Select
@@ -80,22 +83,20 @@ export const PriceFeedSelect = ({ children, feeds }: Props) => {
8083
</SearchField>
8184
<Virtualizer layout={new ListLayout()}>
8285
<ListBox
83-
items={filteredFeeds}
86+
items={sortedFeeds}
8487
className={styles.listbox ?? ""}
8588
// eslint-disable-next-line jsx-a11y/no-autofocus
8689
autoFocus={false}
8790
>
88-
{({ assetClass, id, displaySymbol, icon }) => (
91+
{({ id, displaySymbol }) => (
8992
<ListBoxItem
9093
textValue={displaySymbol}
9194
className={styles.priceFeed ?? ""}
9295
href={`/price-feeds/${encodeURIComponent(id)}`}
93-
data-is-first={id === filteredFeeds[0]?.id ? "" : undefined}
96+
data-is-first={id === sortedFeeds[0]?.id ? "" : undefined}
9497
>
95-
<PriceFeedTag compact symbol={displaySymbol} icon={icon} />
96-
<Badge variant="neutral" style="outline" size="xs">
97-
{assetClass.toUpperCase()}
98-
</Badge>
98+
<PriceFeedTag compact symbol={id} />
99+
<AssetClassTag symbol={id} />
99100
</ListBoxItem>
100101
)}
101102
</ListBox>

apps/insights/src/components/PriceFeed/reference-data.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"use client";
22

3-
import { Badge } from "@pythnetwork/component-library/Badge";
43
import { Table } from "@pythnetwork/component-library/Table";
54
import { useMemo } from "react";
65
import { useCollator } from "react-aria";
76

87
import styles from "./reference-data.module.scss";
8+
import { AssetClassTag } from "../AssetClassTag";
99
import { LiveValue } from "../LivePrices";
1010

1111
type Props = {
@@ -42,11 +42,7 @@ export const ReferenceData = ({ feed }: Props) => {
4242
() =>
4343
[
4444
...Object.entries({
45-
"Asset Type": (
46-
<Badge variant="neutral" style="outline" size="xs">
47-
{feed.assetClass.toUpperCase()}
48-
</Badge>
49-
),
45+
"Asset Type": <AssetClassTag symbol={feed.symbol} />,
5046
Base: feed.base,
5147
Description: feed.description,
5248
Symbol: feed.symbol,

0 commit comments

Comments
 (0)