Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export const GET = async (
const filteredFeeds = feeds.filter((feed) =>
feed.price.priceComponents.some((c) => c.publisher === publisher),
);

return new Response(stringify(filteredFeeds), {
headers: {
"Content-Type": "application/json",
Expand Down
60 changes: 19 additions & 41 deletions apps/insights/src/components/Explanations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,25 @@ export const ExplainPermissioned = ({
scoreTime,
}: {
scoreTime?: Date | undefined;
}) => {
return (
<Explain size="xs" title="Permissioned Feeds">
<p>
This is the number of <b>Price Feeds</b> that a <b>Publisher</b> has
permissions to publish to. The publisher is not necessarily pushing data
for all the feeds they have access to, and some feeds may not be live
yet.
</p>
{scoreTime && <EvaluationTime scoreTime={scoreTime} />}
</Explain>
);
};
}) => (
<Explain size="xs" title="Permissioned Feeds">
<p>
This is the number of <b>Price Feeds</b> that a <b>Publisher</b> has
permissions to publish to. The publisher is not necessarily pushing data
for all the feeds they have access to, and some feeds may not be live yet.
</p>
{scoreTime && <EvaluationTime scoreTime={scoreTime} />}
</Explain>
);

export const ExplainUnpermissioned = () => (
<Explain size="xs" title="Unpermissioned Feeds">
<p>
This is the number of <b>Price Feeds</b> that a <b>Publisher</b> does not
have permissions to publish to.
</p>
</Explain>
);

export const ExplainAverage = ({
scoreTime,
Expand Down Expand Up @@ -96,31 +102,3 @@ export const EvaluationTime = ({ scoreTime }: { scoreTime: Date }) => {
</p>
);
};

export const ExplainActive = () => (
<Explain size="xs" title="Active Feeds">
<p>
This is the number of feeds which the publisher is permissioned for, where
the publisher{"'"}s feed has 50% or better uptime over the last day.
</p>
<NeitherActiveNorInactiveNote />
</Explain>
);

export const ExplainInactive = () => (
<Explain size="xs" title="Inactive Feeds">
<p>
This is the number of feeds which the publisher is permissioned for, but
for which the publisher{"'"}s feed has less than 50% uptime over the last
day.
</p>
<NeitherActiveNorInactiveNote />
</Explain>
);

const NeitherActiveNorInactiveNote = () => (
<p>
Note that a publisher{"'"}s feed may not be considered either active or
inactive if Pyth has not yet calculated quality rankings for it.
</p>
);
2 changes: 1 addition & 1 deletion apps/insights/src/components/FormattedNumber/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useMemo } from "react";
import { useNumberFormatter } from "react-aria";

type Props = Parameters<typeof useNumberFormatter>[0] & {
value: number;
value: number | bigint;
};

export const FormattedNumber = ({ value, ...args }: Props) => {
Expand Down
2 changes: 1 addition & 1 deletion apps/insights/src/components/LivePrices/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "../../hooks/use-live-price-data";
import { usePriceFormatter } from "../../hooks/use-price-formatter";
import type { Cluster } from "../../services/pyth";
import { useLivePublishersData } from '../../hooks/use-live-publishers-data';

export const SKELETON_WIDTH = 20;

Expand Down Expand Up @@ -210,7 +211,6 @@ export const LiveComponentValue = <T extends keyof PriceComponent["latest"]>({
cluster,
}: LiveComponentValueProps<T>) => {
const { current } = useLivePriceComponent(cluster, feedKey, publisherKey);

return current !== undefined || defaultValue !== undefined ? (
(current?.latest[field].toString() ?? defaultValue)
) : (
Expand Down
26 changes: 16 additions & 10 deletions apps/insights/src/components/PriceComponentDrawer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,14 @@ import type { CategoricalChartState } from "recharts/types/chart/types";
import { z } from "zod";

import { Cluster, ClusterToName } from "../../services/pyth";
import type { Status } from "../../status";
import ConformanceReport from "../ConformanceReport/conformance-report";
import type { Interval } from "../ConformanceReport/types";
import { useDownloadReportForFeed } from '../ConformanceReport/use-download-report-for-feed';
import { LiveComponentValue, LiveConfidence, LivePrice } from "../LivePrices";
import { PriceName } from "../PriceName";
import { Score } from "../Score";
import { Status as StatusComponent } from "../Status";
import { StatusLive } from "../Status";
import styles from "./index.module.scss";
import type { Interval } from "../ConformanceReport/types";
import { useDownloadReportForFeed } from "../ConformanceReport/use-download-report-for-feed";

const LineChart = dynamic(
() => import("recharts").then((recharts) => recharts.LineChart),
Expand All @@ -61,7 +60,6 @@ type PriceComponent = {
feedKey: string;
score: number | undefined;
rank: number | undefined;
status: Status;
identifiesPublisher?: boolean | undefined;
firstEvaluation?: Date | undefined;
cluster: Cluster;
Expand Down Expand Up @@ -137,16 +135,20 @@ export const usePriceComponentDrawer = ({
<RouterProvider navigate={navigate}>
<HeadingExtra
identifiesPublisher={identifiesPublisher}
status={component.status}
cluster={component.cluster}
publisherKey={component.publisherKey}
symbol={component.symbol}
feedKey={component.feedKey}
/>
</RouterProvider>
),
headingAfter: (
<div className={styles.badges}>
<StatusComponent status={component.status} />
<StatusLive
cluster={component.cluster}
feedKey={component.feedKey}
publisherKey={component.publisherKey}
/>
</div>
),
contents: (
Expand Down Expand Up @@ -267,14 +269,14 @@ export const usePriceComponentDrawer = ({
};

type HeadingExtraProps = {
status: Status;
identifiesPublisher?: boolean | undefined;
cluster: Cluster;
publisherKey: string;
symbol: string;
feedKey: string;
};

const HeadingExtra = ({ status, ...props }: HeadingExtraProps) => {
const HeadingExtra = ({ feedKey, ...props }: HeadingExtraProps) => {
const downloadReportForFeed = useDownloadReportForFeed();

const handleDownloadReport = useCallback(
Expand All @@ -293,7 +295,11 @@ const HeadingExtra = ({ status, ...props }: HeadingExtraProps) => {
<>
<ConformanceReport onClick={handleDownloadReport} />
<div className={styles.bigScreenBadges}>
<StatusComponent status={status} />
<StatusLive
cluster={props.cluster}
feedKey={feedKey}
publisherKey={props.publisherKey}
/>
</div>
<OpenButton
variant="ghost"
Expand Down
92 changes: 56 additions & 36 deletions apps/insights/src/components/PriceComponentsCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import { SearchInput } from "@pythnetwork/component-library/SearchInput";
import { Select } from "@pythnetwork/component-library/Select";
import { SingleToggleGroup } from "@pythnetwork/component-library/SingleToggleGroup";
import type {
RowConfig,
ColumnConfig,
RowConfig,
SortDescriptor,
} from "@pythnetwork/component-library/Table";
import { Table } from "@pythnetwork/component-library/Table";
import { useLogger } from "@pythnetwork/component-library/useLogger";
import clsx from "clsx";
import { useQueryState, parseAsStringEnum, parseAsBoolean } from "nuqs";
import { parseAsBoolean, parseAsStringEnum, useQueryState } from "nuqs";
import type { ReactNode } from "react";
import { Fragment, Suspense, useMemo, useCallback } from "react";
import { useFilter, useCollator } from "react-aria";
import { Fragment, Suspense, useCallback, useMemo } from "react";
import { useCollator } from "react-aria";

import styles from "./index.module.scss";
import { matchSorter } from 'match-sorter';
import { LivePublishersDataProvider, useLivePublishersData } from '../../hooks/use-live-publishers-data';
import { useQueryParamFilterPagination } from "../../hooks/use-query-param-filter-pagination";
import { Cluster } from "../../services/pyth";
import type { StatusName } from "../../status";
Expand All @@ -34,11 +35,12 @@ import {
import { Explain } from "../Explain";
import { EvaluationTime } from "../Explanations";
import { FormattedNumber } from "../FormattedNumber";
import { LivePrice, LiveConfidence, LiveComponentValue } from "../LivePrices";
import { LiveComponentValue, LiveConfidence, LivePrice } from "../LivePrices";
import { usePriceComponentDrawer } from "../PriceComponentDrawer";
import { PriceName } from "../PriceName";
import { Score } from "../Score";
import { Status as StatusComponent } from "../Status";
import styles from "./index.module.scss";

const SCORE_WIDTH = 32;

Expand Down Expand Up @@ -75,7 +77,7 @@ export type PriceComponent = {
deviationScore: number | undefined;
stalledScore: number | undefined;
cluster: Cluster;
status: StatusType;
status?: StatusType;
feedKey: string;
publisherKey: string;
name: ReactNode;
Expand Down Expand Up @@ -106,6 +108,19 @@ export const PriceComponentsCard = <
}
};

const LiveSlot = ({ feedKey, publisherKey, cluster }: { feedKey: string, publisherKey: string, cluster: Cluster }) => {
const publisherData = useLivePublishersData(feedKey);
if(!publisherData?.slot) {
return <LiveComponentValue
feedKey={feedKey}
publisherKey={publisherKey}
field="publishSlot"
cluster={cluster}
/>
}
return publisherData.slot;
};

export const ResolvedPriceComponentsCard = <
U extends string,
T extends PriceComponent & Record<U, unknown>,
Expand All @@ -119,7 +134,6 @@ export const ResolvedPriceComponentsCard = <
}) => {
const logger = useLogger();
const collator = useCollator();
const filter = useFilter({ sensitivity: "base", usage: "search" });
const { selectComponent } = usePriceComponentDrawer({
components: priceComponents,
identifiesPublisher,
Expand Down Expand Up @@ -158,7 +172,7 @@ export const ResolvedPriceComponentsCard = <
mkPageLink,
} = useQueryParamFilterPagination(
componentsFilteredByStatus,
(component, search) => filter.contains(component.nameAsString, search),
()=>true,
(a, b, { column, direction }) => {
switch (column) {
case "score":
Expand Down Expand Up @@ -186,6 +200,9 @@ export const ResolvedPriceComponentsCard = <
}

case "status": {
if (a.status === undefined || b.status === undefined) {
return 0;
}
const resultByStatus = b.status - a.status;
const result =
resultByStatus === 0
Expand All @@ -200,7 +217,11 @@ export const ResolvedPriceComponentsCard = <
}
}
},
(items) => items,
(items, search) => {
return matchSorter(items, search, {
keys: ["nameAsString","feedKey"],
});
},
{
defaultPageSize: 50,
defaultSort: "name",
Expand Down Expand Up @@ -246,12 +267,7 @@ export const ResolvedPriceComponentsCard = <
/>
),
slot: (
<LiveComponentValue
feedKey={component.feedKey}
publisherKey={component.publisherKey}
field="publishSlot"
cluster={component.cluster}
/>
<LiveSlot feedKey={component.feedKey} publisherKey={component.publisherKey} cluster={component.cluster} />
),
price: (
<LivePrice
Expand All @@ -267,7 +283,9 @@ export const ResolvedPriceComponentsCard = <
cluster={component.cluster}
/>
),
status: <StatusComponent status={component.status} />,
status: component.status !== undefined && (
<StatusComponent status={component.status} />
),
},
})),
[paginatedItems, props.extraColumns, selectComponent],
Expand All @@ -293,25 +311,27 @@ export const ResolvedPriceComponentsCard = <
);

return (
<PriceComponentsCardContents
numResults={numResults}
search={search}
sortDescriptor={sortDescriptor}
numPages={numPages}
page={page}
pageSize={pageSize}
onSearchChange={updateSearch}
onSortChange={updateSortDescriptor}
onPageSizeChange={updatePageSize}
onPageChange={updatePage}
mkPageLink={mkPageLink}
rows={rows}
status={status}
onStatusChange={updateStatus}
showQuality={showQuality}
setShowQuality={updateShowQuality}
{...props}
/>
<LivePublishersDataProvider publisherKey={priceComponents[0]!.publisherKey}>
<PriceComponentsCardContents
numResults={numResults}
search={search}
sortDescriptor={sortDescriptor}
numPages={numPages}
page={page}
pageSize={pageSize}
onSearchChange={updateSearch}
onSortChange={updateSortDescriptor}
onPageSizeChange={updatePageSize}
onPageChange={updatePage}
mkPageLink={mkPageLink}
rows={rows}
status={status}
onStatusChange={updateStatus}
showQuality={showQuality}
setShowQuality={updateShowQuality}
{...props}
/>
</LivePublishersDataProvider>
);
};

Expand Down
Loading
Loading