"use client"; import { PlusMinus } from "@phosphor-icons/react/dist/ssr/PlusMinus"; import type { PriceData, PriceComponent } from "@pythnetwork/client"; import { Skeleton } from "@pythnetwork/component-library/Skeleton"; import { type ReactNode, useMemo } from "react"; import { useNumberFormatter, useDateFormatter } from "react-aria"; import styles from "./index.module.scss"; import { useLivePriceComponent, useLivePriceData, } from "../../hooks/use-live-price-data"; export const SKELETON_WIDTH = 20; export const LivePrice = ({ feedKey, publisherKey, }: { feedKey: string; publisherKey?: string | undefined; }) => publisherKey ? ( ) : ( ); const LiveAggregatePrice = ({ feedKey }: { feedKey: string }) => { const { prev, current } = useLivePriceData(feedKey); return ( ); }; const LiveComponentPrice = ({ feedKey, publisherKey, }: { feedKey: string; publisherKey: string; }) => { const { prev, current } = useLivePriceComponent(feedKey, publisherKey); return ; }; const Price = ({ prev, current, }: { prev?: number | undefined; current?: number | undefined; }) => { const numberFormatter = useNumberFormatter({ maximumFractionDigits: 5 }); return current === undefined ? ( ) : ( {numberFormatter.format(current)} ); }; export const LiveConfidence = ({ feedKey, publisherKey, }: { feedKey: string; publisherKey?: string | undefined; }) => publisherKey === undefined ? ( ) : ( ); const LiveAggregateConfidence = ({ feedKey }: { feedKey: string }) => { const { current } = useLivePriceData(feedKey); return ; }; const LiveComponentConfidence = ({ feedKey, publisherKey, }: { feedKey: string; publisherKey: string; }) => { const { current } = useLivePriceComponent(feedKey, publisherKey); return ; }; const Confidence = ({ confidence }: { confidence?: number | undefined }) => { const numberFormatter = useNumberFormatter({ maximumFractionDigits: 5 }); return ( {confidence === undefined ? ( ) : ( {numberFormatter.format(confidence)} )} ); }; export const LiveLastUpdated = ({ feedKey }: { feedKey: string }) => { const { current } = useLivePriceData(feedKey); const formatterWithDate = useDateFormatter({ dateStyle: "short", timeStyle: "medium", }); const formatterWithoutDate = useDateFormatter({ timeStyle: "medium", }); const formattedTimestamp = useMemo(() => { if (current) { const timestamp = new Date(Number(current.timestamp * 1000n)); return isToday(timestamp) ? formatterWithoutDate.format(timestamp) : formatterWithDate.format(timestamp); } else { return; } }, [current, formatterWithDate, formatterWithoutDate]); return formattedTimestamp ?? ; }; type LiveValueProps = { field: T; feedKey: string; defaultValue?: ReactNode | undefined; }; export const LiveValue = ({ feedKey, field, defaultValue, }: LiveValueProps) => { const { current } = useLivePriceData(feedKey); return current?.[field]?.toString() ?? defaultValue; }; type LiveComponentValueProps = { field: T; feedKey: string; publisherKey: string; defaultValue?: ReactNode | undefined; }; export const LiveComponentValue = ({ feedKey, field, publisherKey, defaultValue, }: LiveComponentValueProps) => { const { current } = useLivePriceComponent(feedKey, publisherKey); return current?.latest[field].toString() ?? defaultValue; }; const isToday = (date: Date) => { const now = new Date(); return ( date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate() ); }; const getChangeDirection = ( prevPrice: number | undefined, price: number, ): ChangeDirection => { if (prevPrice === undefined || prevPrice === price) { return "flat"; } else if (prevPrice < price) { return "up"; } else { return "down"; } }; type ChangeDirection = "up" | "down" | "flat";