import { lazy, Suspense, useState } from "react"; import { useTranslation } from "react-i18next"; import { useQuery } from "@tanstack/react-query"; import { EyeOff, History, UserPlus } from "lucide-react"; import { qk } from "../../lib/queryKeys"; import { api, type WatchedChannel } from "../../lib/api"; import { formatDate } from "../../lib/format"; import { focusSettingsTab } from "../../lib/settingsTab"; import { useNavigationActions } from "../../features/navigation/navigationContext"; import Tooltip from "../../components/ui/Tooltip"; import DataTable, { type Column } from "../../components/ui/DataTable"; import { LoadingState, StateMessage } from "../../components/ui/QueryState"; import { Button } from "../../components/ui/Button"; import ChannelLink from "./ChannelLink"; import AboutCell from "./AboutCell"; import ChannelLayoutGrid from "./ChannelLayoutGrid"; import { subsColumn } from "./channelColumns"; import { useChannelLayout } from "./channelsContext"; import { useWatchedChannelActions } from "./useWatchedChannelActions"; const WatchedVideosDialog = lazy(() => import("./WatchedVideosDialog")); // How many channels the list shows at first, and how many more each "Show more" adds. The list is a // ranking — the most watched first — so a short top is the point: past it, the reader asks for more. const WATCHED_PAGE = 20; // The Channel manager's "Discover from your history" tab: the channels the user's Takeout watch // history shows them watching often but not following, the most watched first. A row's count opens // the videos it counts, ready to play — a quick look at what the channel was, before following it. export default function WatchedChannels({ canWrite, search, onOpenWizard, onViewChannel, }: { canWrite: boolean; // Client-side name filter from the shared top SearchBar (matches title + handle). search: string; onOpenWizard: () => void; onViewChannel: (id: string, name: string) => void; }) { const { t, i18n } = useTranslation(); const lang = i18n.language; const layout = useChannelLayout(); const { setPage } = useNavigationActions(); const [shown, setShown] = useState(WATCHED_PAGE); const [open, setOpen] = useState(null); const actions = useWatchedChannelActions(onOpenWizard); const query = useQuery({ queryKey: qk.watchedChannels(), queryFn: api.watchedChannels }); const q = search.trim().toLowerCase(); const rows = (query.data?.channels ?? []).filter( (c) => !q || `${c.title ?? ""} ${c.handle ?? ""}`.toLowerCase().includes(q), ); const visible = rows.slice(0, shown); // The order is the ranking itself, so no column sorts: a header sort would only reorder the top // twenty and hide that it is a top. const columns: Column[] = [ { key: "actions", header: t("channels.cols.actions"), align: "left", nowrap: true, cardLabel: false, render: (c) => (
), }, { key: "channel", header: t("channels.cols.channel"), cardPrimary: true, render: (c) => (
onViewChannel(c.id, c.title ?? c.id)} />
), }, { key: "about", header: t("channels.cols.about"), hideInCard: true, render: (c) => , }, { ...subsColumn(t, lang), sortable: false }, { key: "watched", header: t("channels.watched.cols.watched"), align: "right", nowrap: true, render: (c) => ( ), }, { key: "last", header: t("channels.watched.cols.last"), align: "right", nowrap: true, render: (c) => ( {formatDate(c.last_watched_at, lang)} ), }, ]; const pending = query.data?.pending ?? 0; const more = Math.min(WATCHED_PAGE, rows.length - visible.length); const body = query.isError && !query.data ? ( query.refetch()}> {t("common.loadError")} ) : query.isLoading ? ( ) : !query.data?.imported ? ( // The message and the way to the upload, one line: StateMessage lays its content in a row. {t("channels.watched.notImported")} ) : layout === "table" ? ( c.id} pager={false} emptyText={t("channels.watched.empty")} keyboardNav narrowedBy={q} /> ) : ( c.id} aboutKey="about" actionsKey="actions" emptyText={t("channels.watched.empty")} /> ); return (

{t("channels.watched.intro")}

{pending > 0 && (

{t("channels.watched.pending", { count: pending })}

)} {body} {rows.length > 0 && (
{t("channels.watched.shown", { shown: visible.length, total: rows.length })} {more > 0 && ( )}
)} {open && ( setOpen(null)} /> )}
); }