Преглед изворни кода

fix(insights): don't show very small numbers as zero

Connor Prussin пре 5 месеци
родитељ
комит
3184e6b7a1

+ 22 - 20
apps/insights/src/components/LivePrices/index.tsx

@@ -5,13 +5,14 @@ import type { PriceData, PriceComponent } from "@pythnetwork/client";
 import { Skeleton } from "@pythnetwork/component-library/Skeleton";
 import type { ReactNode } from "react";
 import { useMemo } from "react";
-import { useNumberFormatter, useDateFormatter } from "react-aria";
+import { useDateFormatter } from "react-aria";
 
 import styles from "./index.module.scss";
 import {
   useLivePriceComponent,
   useLivePriceData,
 } from "../../hooks/use-live-price-data";
+import { usePriceFormatter } from "../../hooks/use-price-formatter";
 import type { Cluster } from "../../services/pyth";
 
 export const SKELETON_WIDTH = 20;
@@ -66,20 +67,17 @@ const Price = ({
 }: {
   prev?: number | undefined;
   current?: number | undefined;
-}) => {
-  const numberFormatter = useNumberFormatter({ maximumFractionDigits: 5 });
-
-  return current === undefined ? (
+}) =>
+  current === undefined ? (
     <Skeleton width={SKELETON_WIDTH} />
   ) : (
     <span
       className={styles.price}
       data-direction={prev ? getChangeDirection(prev, current) : "flat"}
     >
-      {numberFormatter.format(current)}
+      <FormattedPriceValue n={current} />
     </span>
   );
-};
 
 export const LiveConfidence = ({
   publisherKey,
@@ -119,19 +117,23 @@ const LiveComponentConfidence = ({
   return <Confidence confidence={current?.latest.confidence} />;
 };
 
-const Confidence = ({ confidence }: { confidence?: number | undefined }) => {
-  const numberFormatter = useNumberFormatter({ maximumFractionDigits: 5 });
-
-  return (
-    <span className={styles.confidence}>
-      <PlusMinus className={styles.plusMinus} />
-      {confidence === undefined ? (
-        <Skeleton width={SKELETON_WIDTH} />
-      ) : (
-        <span>{numberFormatter.format(confidence)}</span>
-      )}
-    </span>
-  );
+const Confidence = ({ confidence }: { confidence?: number | undefined }) => (
+  <span className={styles.confidence}>
+    <PlusMinus className={styles.plusMinus} />
+    {confidence === undefined ? (
+      <Skeleton width={SKELETON_WIDTH} />
+    ) : (
+      <span>
+        <FormattedPriceValue n={confidence} />
+      </span>
+    )}
+  </span>
+);
+
+const FormattedPriceValue = ({ n }: { n: number }) => {
+  const formatter = usePriceFormatter();
+
+  return useMemo(() => formatter.format(n), [n, formatter]);
 };
 
 export const LiveLastUpdated = ({

+ 6 - 1
apps/insights/src/components/PriceFeed/chart.tsx

@@ -11,6 +11,7 @@ import { z } from "zod";
 
 import styles from "./chart.module.scss";
 import { useLivePriceData } from "../../hooks/use-live-price-data";
+import { usePriceFormatter } from "../../hooks/use-price-formatter";
 import { Cluster } from "../../services/pyth";
 
 type Props = {
@@ -44,6 +45,7 @@ const useChartElem = (symbol: string, feedId: string) => {
   const chartRef = useRef<ChartRefContents | undefined>(undefined);
   const earliestDateRef = useRef<bigint | undefined>(undefined);
   const isBackfilling = useRef(false);
+  const priceFormatter = usePriceFormatter();
 
   const backfillData = useCallback(() => {
     if (!isBackfilling.current && earliestDateRef.current) {
@@ -113,6 +115,9 @@ const useChartElem = (symbol: string, feedId: string) => {
           timeVisible: true,
           secondsVisible: true,
         },
+        localization: {
+          priceFormatter: priceFormatter.format,
+        },
       });
 
       const price = chart.addSeries(LineSeries, { priceFormat });
@@ -141,7 +146,7 @@ const useChartElem = (symbol: string, feedId: string) => {
         chart.remove();
       };
     }
-  }, [backfillData]);
+  }, [backfillData, priceFormatter]);
 
   useEffect(() => {
     if (current && chartRef.current) {

+ 40 - 0
apps/insights/src/hooks/use-price-formatter.ts

@@ -0,0 +1,40 @@
+import { useCallback, useMemo } from "react";
+import { useNumberFormatter } from "react-aria";
+
+export const usePriceFormatter = () => {
+  const bigNumberFormatter = useNumberFormatter({ maximumFractionDigits: 2 });
+  const smallNumberFormatter = useNumberFormatter({
+    maximumSignificantDigits: 5,
+  });
+  const format = useCallback(
+    (n: number) =>
+      n >= 1000
+        ? bigNumberFormatter.format(n)
+        : formatToSubscriptNumber(smallNumberFormatter.format(n)),
+    [bigNumberFormatter, smallNumberFormatter],
+  );
+  return useMemo(() => ({ format }), [format]);
+};
+
+const formatToSubscriptNumber = (numString: string) => {
+  const parts = numString.split(".");
+
+  const [integerPart, decimalPart] = parts;
+  if (integerPart && decimalPart) {
+    const zerosCount =
+      decimalPart.length - decimalPart.replace(/^0+/, "").length;
+
+    return zerosCount < 5
+      ? numString
+      : integerPart +
+          "." +
+          "0" +
+          (zerosCount > 9
+            ? String.fromCodePoint(0x20_80 + Math.floor(zerosCount / 10))
+            : "") +
+          String.fromCodePoint(0x20_80 + (zerosCount % 10)) +
+          decimalPart.replace(/^0+/, "");
+  } else {
+    return numString;
+  }
+};