Skip to content

feat(insights): optimize page payload sizes #2217

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

Merged
merged 1 commit into from
Dec 30, 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
53 changes: 18 additions & 35 deletions apps/insights/src/components/ChangePercent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const ONE_HOUR_IN_MS = 60 * ONE_MINUTE_IN_MS;
const REFRESH_YESTERDAYS_PRICES_INTERVAL = ONE_HOUR_IN_MS;

type Props = Omit<ComponentProps<typeof YesterdaysPricesContext>, "value"> & {
feeds: (Feed & { symbol: string })[];
feeds: Record<string, string>;
};

const YesterdaysPricesContext = createContext<
Expand All @@ -23,7 +23,7 @@ const YesterdaysPricesContext = createContext<

export const YesterdaysPricesProvider = ({ feeds, ...props }: Props) => {
const state = useData(
["yesterdaysPrices", feeds.map((feed) => feed.symbol)],
["yesterdaysPrices", Object.keys(feeds)],
() => getYesterdaysPrices(feeds),
{
refreshInterval: REFRESH_YESTERDAYS_PRICES_INTERVAL,
Expand All @@ -34,22 +34,16 @@ export const YesterdaysPricesProvider = ({ feeds, ...props }: Props) => {
};

const getYesterdaysPrices = async (
feeds: (Feed & { symbol: string })[],
feeds: Props["feeds"],
): Promise<Map<string, number>> => {
const url = new URL("/yesterdays-prices", window.location.origin);
for (const feed of feeds) {
url.searchParams.append("symbols", feed.symbol);
for (const symbol of Object.keys(feeds)) {
url.searchParams.append("symbols", symbol);
}
const response = await fetch(url);
const data: unknown = await response.json();
const data = yesterdaysPricesSchema.parse(await response.json());
return new Map(
Object.entries(yesterdaysPricesSchema.parse(data)).map(
([symbol, value]) => [
feeds.find((feed) => feed.symbol === symbol)?.product.price_account ??
"",
value,
],
),
Object.entries(data).map(([symbol, value]) => [feeds[symbol] ?? "", value]),
);
};

Expand All @@ -67,39 +61,28 @@ const useYesterdaysPrices = () => {

type ChangePercentProps = {
className?: string | undefined;
feed: Feed;
feedKey: string;
};

type Feed = {
product: {
price_account: string;
};
};

export const ChangePercent = ({ feed, className }: ChangePercentProps) => {
export const ChangePercent = ({ feedKey, className }: ChangePercentProps) => {
const yesterdaysPriceState = useYesterdaysPrices();

switch (yesterdaysPriceState.type) {
case StateType.Error: {
// eslint-disable-next-line unicorn/no-null
return null;
}

case StateType.Error:
case StateType.Loading:
case StateType.NotLoaded: {
return <ChangeValue className={className} isLoading />;
}

case StateType.Loaded: {
const yesterdaysPrice = yesterdaysPriceState.data.get(
feed.product.price_account,
);
// eslint-disable-next-line unicorn/no-null
return yesterdaysPrice === undefined ? null : (
const yesterdaysPrice = yesterdaysPriceState.data.get(feedKey);
return yesterdaysPrice === undefined ? (
<ChangeValue className={className} isLoading />
) : (
<ChangePercentLoaded
className={className}
priorPrice={yesterdaysPrice}
feed={feed}
feedKey={feedKey}
/>
);
}
Expand All @@ -109,15 +92,15 @@ export const ChangePercent = ({ feed, className }: ChangePercentProps) => {
type ChangePercentLoadedProps = {
className?: string | undefined;
priorPrice: number;
feed: Feed;
feedKey: string;
};

const ChangePercentLoaded = ({
className,
priorPrice,
feed,
feedKey,
}: ChangePercentLoadedProps) => {
const currentPrice = useLivePrice(feed);
const currentPrice = useLivePrice(feedKey);

return currentPrice === undefined ? (
<ChangeValue className={className} isLoading />
Expand Down
17 changes: 5 additions & 12 deletions apps/insights/src/components/FeedKey/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import { toHex, truncateHex } from "../../hex";
import { CopyButton } from "../CopyButton";

type OwnProps = {
feed: {
product: {
price_account: string;
};
};
feedKey: string;
};

type Props = Omit<
Expand All @@ -17,15 +13,12 @@ type Props = Omit<
> &
OwnProps;

export const FeedKey = ({ feed, ...props }: Props) => {
const key = useMemo(
() => toHex(feed.product.price_account),
[feed.product.price_account],
);
const truncatedKey = useMemo(() => truncateHex(key), [key]);
export const FeedKey = ({ feedKey, ...props }: Props) => {
const hexKey = useMemo(() => toHex(feedKey), [feedKey]);
const truncatedKey = useMemo(() => truncateHex(hexKey), [hexKey]);

return (
<CopyButton text={key} {...props}>
<CopyButton text={hexKey} {...props}>
{truncatedKey}
</CopyButton>
);
Expand Down
43 changes: 18 additions & 25 deletions apps/insights/src/components/LivePrices/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,35 +38,28 @@ type LivePricesProviderProps = Omit<
"value"
>;

export const LivePricesProvider = ({ ...props }: LivePricesProviderProps) => {
export const LivePricesProvider = (props: LivePricesProviderProps) => {
const priceData = usePriceData();

return <LivePricesContext value={priceData} {...props} />;
};

type Feed = {
product: {
price_account: string;
};
};

export const useLivePrice = (feed: Feed) => {
const { price_account } = feed.product;
export const useLivePrice = (feedKey: string) => {
const { priceData, addSubscription, removeSubscription } = useLivePrices();

useEffect(() => {
addSubscription(price_account);
addSubscription(feedKey);
return () => {
removeSubscription(price_account);
removeSubscription(feedKey);
};
}, [addSubscription, removeSubscription, price_account]);
}, [addSubscription, removeSubscription, feedKey]);

return priceData.get(price_account);
return priceData.get(feedKey);
};

export const LivePrice = ({ feed }: { feed: Feed }) => {
export const LivePrice = ({ feedKey }: { feedKey: string }) => {
const numberFormatter = useNumberFormatter({ maximumSignificantDigits: 5 });
const price = useLivePrice(feed);
const price = useLivePrice(feedKey);

return price === undefined ? (
<Skeleton width={SKELETON_WIDTH} />
Expand All @@ -77,9 +70,9 @@ export const LivePrice = ({ feed }: { feed: Feed }) => {
);
};

export const LiveConfidence = ({ feed }: { feed: Feed }) => {
export const LiveConfidence = ({ feedKey }: { feedKey: string }) => {
const numberFormatter = useNumberFormatter({ maximumSignificantDigits: 5 });
const price = useLivePrice(feed);
const price = useLivePrice(feedKey);

return (
<span className={styles.confidence}>
Expand All @@ -93,8 +86,8 @@ export const LiveConfidence = ({ feed }: { feed: Feed }) => {
);
};

export const LiveLastUpdated = ({ feed }: { feed: Feed }) => {
const price = useLivePrice(feed);
export const LiveLastUpdated = ({ feedKey }: { feedKey: string }) => {
const price = useLivePrice(feedKey);
const formatterWithDate = useDateFormatter({
dateStyle: "short",
timeStyle: "medium",
Expand All @@ -118,18 +111,18 @@ export const LiveLastUpdated = ({ feed }: { feed: Feed }) => {

type LiveValueProps<T extends keyof PriceData> = {
field: T;
feed: Feed & {
price: Record<T, ReactNode>;
};
feedKey: string;
defaultValue?: ReactNode | undefined;
};

export const LiveValue = <T extends keyof PriceData>({
feed,
feedKey,
field,
defaultValue,
}: LiveValueProps<T>) => {
const price = useLivePrice(feed);
const price = useLivePrice(feedKey);

return price?.[field]?.toString() ?? feed.price[field];
return price?.[field]?.toString() ?? defaultValue;
};

const isToday = (date: Date) => {
Expand Down
65 changes: 48 additions & 17 deletions apps/insights/src/components/PriceFeed/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
LiveLastUpdated,
LiveValue,
} from "../LivePrices";
import { PriceFeedIcon } from "../PriceFeedIcon";
import { PriceFeedTag } from "../PriceFeedTag";
import { TabPanel, TabRoot, Tabs } from "../Tabs";

Expand Down Expand Up @@ -61,33 +62,57 @@ export const PriceFeedLayout = async ({ children, params }: Props) => {
feeds={data
.filter((feed) => feed.symbol !== symbol)
.map((feed) => ({
id: encodeURIComponent(feed.symbol),
id: feed.symbol,
key: toHex(feed.product.price_account),
displaySymbol: feed.product.display_symbol,
name: <PriceFeedTag compact feed={feed} />,
assetClassText: feed.product.asset_type,
assetClass: (
<Badge variant="neutral" style="outline" size="xs">
{feed.product.asset_type.toUpperCase()}
</Badge>
),
icon: <PriceFeedIcon symbol={feed.symbol} />,
assetClass: feed.product.asset_type,
}))}
>
<PriceFeedTag feed={feed} />
<PriceFeedTag
symbol={feed.product.display_symbol}
description={feed.product.description}
icon={<PriceFeedIcon symbol={feed.symbol} />}
/>
</PriceFeedSelect>
<div className={styles.rightGroup}>
<FeedKey
variant="ghost"
size="sm"
className={styles.feedKey ?? ""}
feed={feed}
feedKey={feed.product.price_account}
/>
<DrawerTrigger>
<Button variant="outline" size="sm" beforeIcon={ListDashes}>
Reference Data
</Button>
<Drawer fill title="Reference Data">
<ReferenceData feed={feed} />
<ReferenceData
feed={{
symbol: feed.symbol,
feedKey: feed.product.price_account,
assetClass: feed.product.asset_type,
base: feed.product.base,
description: feed.product.description,
country: feed.product.country,
quoteCurrency: feed.product.quote_currency,
tenor: feed.product.tenor,
cmsSymbol: feed.product.cms_symbol,
cqsSymbol: feed.product.cqs_symbol,
nasdaqSymbol: feed.product.nasdaq_symbol,
genericSymbol: feed.product.generic_symbol,
weeklySchedule: feed.product.weekly_schedule,
schedule: feed.product.schedule,
contractId: feed.product.contract_id,
displaySymbol: feed.product.display_symbol,
exponent: feed.price.exponent,
numComponentPrices: feed.price.numComponentPrices,
numQuoters: feed.price.numQuoters,
minPublishers: feed.price.minPublishers,
lastSlot: feed.price.lastSlot,
validSlot: feed.price.validSlot,
}}
/>
</Drawer>
</DrawerTrigger>
</div>
Expand All @@ -96,11 +121,11 @@ export const PriceFeedLayout = async ({ children, params }: Props) => {
<StatCard
variant="primary"
header="Aggregated Price"
stat={<LivePrice feed={feed} />}
stat={<LivePrice feedKey={feed.product.price_account} />}
/>
<StatCard
header="Confidence"
stat={<LiveConfidence feed={feed} />}
stat={<LiveConfidence feedKey={feed.product.price_account} />}
corner={
<AlertTrigger>
<Button
Expand Down Expand Up @@ -135,14 +160,16 @@ export const PriceFeedLayout = async ({ children, params }: Props) => {
<StatCard
header="1-Day Price Change"
stat={
<YesterdaysPricesProvider feeds={[feed]}>
<ChangePercent feed={feed} />
<YesterdaysPricesProvider
feeds={{ [feed.symbol]: feed.product.price_account }}
>
<ChangePercent feedKey={feed.product.price_account} />
</YesterdaysPricesProvider>
}
/>
<StatCard
header="Last Updated"
stat={<LiveLastUpdated feed={feed} />}
stat={<LiveLastUpdated feedKey={feed.product.price_account} />}
/>
</section>
</section>
Expand All @@ -158,7 +185,11 @@ export const PriceFeedLayout = async ({ children, params }: Props) => {
<div className={styles.priceComponentsTabLabel}>
<span>Publishers</span>
<Badge size="xs" style="filled" variant="neutral">
<LiveValue feed={feed} field="numComponentPrices" />
<LiveValue
feedKey={feed.product.price_account}
field="numComponentPrices"
defaultValue={feed.price.numComponentPrices}
/>
</Badge>
</div>
),
Expand Down
Loading
Loading