feat(managers): shared sort across the table and card layouts
Cards have no column headers to sort by, so lift the sort out of DataTable into one shared state that BOTH views drive and reflect. New lib/columnSort.ts holds the comparator + click-cycle (one source of truth); DataTable gains an optional controlled sort (sort + onSortChange) and falls back to its internal persistKey sort when they're omitted, so every other table is untouched. The subscribed sort lives in ChannelsProvider, discovery keeps its own (its columns differ); a new ColumnSortControl dropdown (options derived from the columns' sortable metadata, feed-style + asc/desc toggle) drives it in cards mode. Per-account, persisted, per-tab.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { UserPlus } from "lucide-react";
|
||||
import { api, type DiscoveredChannel } from "../lib/api";
|
||||
import { accountKey, LS } from "../lib/storage";
|
||||
import { accountKey, LS, readAccount, writeAccount } from "../lib/storage";
|
||||
import { formatCountOrDash } from "../lib/format";
|
||||
import { sortRows, type SortState } from "../lib/columnSort";
|
||||
import { subsColumn } from "./channelColumns";
|
||||
import { notify } from "../lib/notifications";
|
||||
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
|
||||
@@ -12,6 +14,7 @@ import ChannelLink from "./ChannelLink";
|
||||
import AboutCell from "./AboutCell";
|
||||
import DataTable, { type Column } from "./DataTable";
|
||||
import ChannelLayoutGrid from "./ChannelLayoutGrid";
|
||||
import ColumnSortControl from "./ColumnSortControl";
|
||||
import { PageToolbar } from "./PageShell";
|
||||
import { useConfirm } from "./ConfirmProvider";
|
||||
import { useChannels } from "./ChannelsProvider";
|
||||
@@ -34,8 +37,16 @@ export default function ChannelDiscovery({
|
||||
const { t } = useTranslation();
|
||||
const qc = useQueryClient();
|
||||
const confirm = useConfirm();
|
||||
// The table/cards/rows density is shared with the Subscriptions tab (ChannelsProvider).
|
||||
// The table/cards density is shared with the Subscriptions tab (ChannelsProvider). The sort is
|
||||
// this tab's own (its columns differ), lifted out of the table so the card layout shares it.
|
||||
const { layout } = useChannels();
|
||||
const [sort, setSortState] = useState<SortState>(() =>
|
||||
readAccount<SortState>(LS.channelDiscoverySort, null),
|
||||
);
|
||||
const setSort = (s: SortState) => {
|
||||
setSortState(s);
|
||||
writeAccount(LS.channelDiscoverySort, s);
|
||||
};
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ["discovered-channels"],
|
||||
@@ -173,8 +184,20 @@ export default function ChannelDiscovery({
|
||||
<>
|
||||
{/* Intro + the table controls are fixed chrome; only the table rows scroll (sticky header). */}
|
||||
<PageToolbar>
|
||||
<div className="px-4 pt-3 max-w-[96rem] mx-auto">
|
||||
<div className="px-4 pt-3 max-w-[96rem] mx-auto flex items-start justify-between gap-4">
|
||||
<p className="text-xs text-muted leading-relaxed">{t("channels.discovery.intro")}</p>
|
||||
{/* Cards has no column headers, so a dropdown drives the sort (table uses the headers). */}
|
||||
{layout === "cards" && (
|
||||
<ColumnSortControl
|
||||
columns={columns}
|
||||
sort={sort}
|
||||
onChange={setSort}
|
||||
label={t("channels.sort.label")}
|
||||
defaultLabel={t("channels.sort.default")}
|
||||
ascLabel={t("channels.sort.asc")}
|
||||
descLabel={t("channels.sort.desc")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</PageToolbar>
|
||||
<div className="px-4 pb-4 max-w-[96rem] mx-auto">
|
||||
@@ -189,11 +212,13 @@ export default function ChannelDiscovery({
|
||||
controlsPosition="both"
|
||||
controlsInBand
|
||||
controlsBandClassName="px-4 max-w-[96rem] mx-auto"
|
||||
sort={sort}
|
||||
onSortChange={setSort}
|
||||
emptyText={t("channels.discovery.empty")}
|
||||
/>
|
||||
) : (
|
||||
<ChannelLayoutGrid
|
||||
rows={rows}
|
||||
rows={sortRows(rows, columns, sort)}
|
||||
columns={columns}
|
||||
rowKey={(c) => c.id}
|
||||
aboutKey="about"
|
||||
|
||||
Reference in New Issue
Block a user