"use client"; import { type ComponentProps, createContext, use } from "react"; import { z } from "zod"; import { StateType, useData } from "../../hooks/use-data"; import { useLivePriceData } from "../../hooks/use-live-price-data"; import { ChangePercent } from "../ChangePercent"; const ONE_SECOND_IN_MS = 1000; const ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS; const ONE_HOUR_IN_MS = 60 * ONE_MINUTE_IN_MS; const REFRESH_YESTERDAYS_PRICES_INTERVAL = ONE_HOUR_IN_MS; type YesterdaysPricesProviderProps = Omit< ComponentProps, "value" > & { feeds: Record; }; const YesterdaysPricesContext = createContext< undefined | ReturnType>> >(undefined); export const YesterdaysPricesProvider = ({ feeds, ...props }: YesterdaysPricesProviderProps) => { const state = useData( ["yesterdaysPrices", Object.keys(feeds)], () => getYesterdaysPrices(feeds), { refreshInterval: REFRESH_YESTERDAYS_PRICES_INTERVAL, }, ); return ; }; const getYesterdaysPrices = async ( feeds: YesterdaysPricesProviderProps["feeds"], ): Promise> => { const url = new URL("/yesterdays-prices", window.location.origin); for (const symbol of Object.keys(feeds)) { url.searchParams.append("symbols", symbol); } const response = await fetch(url); const data = yesterdaysPricesSchema.parse(await response.json()); return new Map( Object.entries(data).map(([symbol, value]) => [feeds[symbol] ?? "", value]), ); }; const yesterdaysPricesSchema = z.record(z.string(), z.number()); const useYesterdaysPrices = () => { const state = use(YesterdaysPricesContext); if (state) { return state; } else { throw new YesterdaysPricesNotInitializedError(); } }; type Props = { className?: string | undefined; feedKey: string; }; export const PriceFeedChangePercent = ({ feedKey, className }: Props) => { const yesterdaysPriceState = useYesterdaysPrices(); switch (yesterdaysPriceState.type) { case StateType.Error: case StateType.Loading: case StateType.NotLoaded: { return ; } case StateType.Loaded: { const yesterdaysPrice = yesterdaysPriceState.data.get(feedKey); return yesterdaysPrice === undefined ? ( ) : ( ); } } }; type PriceFeedChangePercentLoadedProps = { className?: string | undefined; priorPrice: number; feedKey: string; }; const PriceFeedChangePercentLoaded = ({ className, priorPrice, feedKey, }: PriceFeedChangePercentLoadedProps) => { const { current } = useLivePriceData(feedKey); return current === undefined ? ( ) : ( ); }; class YesterdaysPricesNotInitializedError extends Error { constructor() { super( "This component must be contained within a ", ); this.name = "YesterdaysPricesNotInitializedError"; } }