"use client";
import { Broadcast } from "@phosphor-icons/react/dist/ssr/Broadcast";
import { Badge } from "@pythnetwork/component-library/Badge";
import { Card } from "@pythnetwork/component-library/Card";
import { Link } from "@pythnetwork/component-library/Link";
import { Paginator } from "@pythnetwork/component-library/Paginator";
import { SearchInput } from "@pythnetwork/component-library/SearchInput";
import {
type RowConfig,
type SortDescriptor,
Table,
} from "@pythnetwork/component-library/Table";
import { type ReactNode, Suspense, useMemo } from "react";
import { useFilter, useCollator } from "react-aria";
import { useQueryParamFilterPagination } from "../../hooks/use-query-param-filter-pagination";
import { ExplainActive, ExplainInactive } from "../Explanations";
import { NoResults } from "../NoResults";
import { PublisherTag } from "../PublisherTag";
import { Ranking } from "../Ranking";
import rootStyles from "../Root/index.module.scss";
import { Score } from "../Score";
const PUBLISHER_SCORE_WIDTH = 38;
type Props = {
className?: string | undefined;
publishers: Publisher[];
explainAverage: ReactNode;
};
type Publisher = {
id: string;
ranking: number;
activeFeeds: number;
inactiveFeeds: number;
averageScore: number;
} & (
| { name: string; icon: ReactNode }
| { name?: undefined; icon?: undefined }
);
export const PublishersCard = ({ publishers, ...props }: Props) => (
}>
);
const ResolvedPublishersCard = ({ publishers, ...props }: Props) => {
const collator = useCollator();
const filter = useFilter({ sensitivity: "base", usage: "search" });
const {
search,
sortDescriptor,
page,
pageSize,
updateSearch,
updateSortDescriptor,
updatePage,
updatePageSize,
paginatedItems,
numResults,
numPages,
mkPageLink,
} = useQueryParamFilterPagination(
publishers,
(publisher, search) =>
filter.contains(publisher.id, search) ||
(publisher.name !== undefined && filter.contains(publisher.name, search)),
(a, b, { column, direction }) => {
switch (column) {
case "ranking":
case "activeFeeds":
case "inactiveFeeds":
case "averageScore": {
return (
(direction === "descending" ? -1 : 1) * (a[column] - b[column])
);
}
case "name": {
return (
(direction === "descending" ? -1 : 1) *
collator.compare(a.name ?? a.id, b.name ?? b.id)
);
}
default: {
return (
(direction === "descending" ? -1 : 1) * (a.ranking - b.ranking)
);
}
}
},
{ defaultSort: "ranking" },
);
const rows = useMemo(
() =>
paginatedItems.map(
({
id,
ranking,
averageScore,
activeFeeds,
inactiveFeeds,
...publisher
}) => ({
id,
href: `/publishers/${id}`,
data: {
ranking: {ranking},
name: (
),
activeFeeds: (
{activeFeeds}
),
inactiveFeeds: (
{inactiveFeeds}
),
averageScore: (
),
},
}),
),
[paginatedItems],
);
return (
);
};
type PublishersCardContentsProps = Pick &
(
| { isLoading: true }
| {
isLoading?: false;
numResults: number;
search: string;
sortDescriptor: SortDescriptor;
numPages: number;
page: number;
pageSize: number;
onSearchChange: (newSearch: string) => void;
onSortChange: (newSort: SortDescriptor) => void;
onPageSizeChange: (newPageSize: number) => void;
onPageChange: (newPage: number) => void;
mkPageLink: (page: number) => string;
rows: RowConfig<
"ranking" | "name" | "activeFeeds" | "inactiveFeeds" | "averageScore"
>[];
}
);
const PublishersCardContents = ({
className,
explainAverage,
...props
}: PublishersCardContentsProps) => (
}
title={
<>
Publishers
{!props.isLoading && (
{props.numResults}
)}
>
}
toolbar={
}
{...(!props.isLoading && {
footer: (
),
})}
>
,
allowsSorting: true,
},
{
id: "name",
name: "NAME / ID",
isRowHeader: true,
alignment: "left",
loadingSkeleton: ,
allowsSorting: true,
},
{
id: "activeFeeds",
name: (
<>
ACTIVE FEEDS
>
),
alignment: "center",
width: 30,
allowsSorting: true,
},
{
id: "inactiveFeeds",
name: (
<>
INACTIVE FEEDS
>
),
alignment: "center",
width: 30,
allowsSorting: true,
},
{
id: "averageScore",
name: (
<>
AVERAGE SCORE
{explainAverage}
>
),
alignment: "right",
width: PUBLISHER_SCORE_WIDTH,
loadingSkeleton: ,
allowsSorting: true,
},
]}
{...(props.isLoading
? {
isLoading: true,
}
: {
rows: props.rows,
sortDescriptor: props.sortDescriptor,
onSortChange: props.onSortChange,
emptyState: (
{
props.onSearchChange("");
}}
/>
),
})}
/>
);