import type { SubscriptionOverviewItem } from "@umt/contracts"; import { Link } from "react-router"; import { consolePath } from "~/components/console/nav.ts"; import { StatusBadge } from "~/components/console/status-badge.tsx"; import { CoverPlaceholder, relTime } from "~/components/library/parts.tsx"; import { cn } from "~/lib/utils.ts"; import { SubscriptionMenu } from "./actions.tsx"; import { autoFillText, foldNames, progressParts, progressText, SUB_KIND_LABEL, seasonLabel, slotBadge, } from "./model.ts"; // 总览卡片 / 列表行(设计 §1.2)。点卡片 = 查看对比。 type Item = SubscriptionOverviewItem; export const itemTitle = (x: Item) => x.mediaEntityTitle ?? x.titleQuery ?? "未命名"; /** 状态角标:只标 暂停 / 待定 / 洗版中 / 等待新季 */ function Flags({ x }: { x: Item }) { return ( <> {x.group === "paused" ? 暂停 : null} {x.group === "pending" ? ( {x.mediaEntityId ? "待定" : "不在媒体库"} ) : null} {x.upgrading ? 洗版中 : null} {x.awaitingNewSeason ? 等待新季 : null} ); } /** 进度:数字 + 已入库/在途两段细条;总未知写"持续追踪" */ export function ProgressBlock({ x, className }: { x: Pick; className?: string }) { const parts = progressParts(x.progress); const unknown = x.progress.total === null; return (
{progressText(x.progress)} {x.progress.inflight > 0 ? ( · 在途 {x.progress.inflight} ) : null} {unknown ? 持续追踪 : null}
{unknown ? null : ( <>
)}
); } /** 只按标题订阅、没关联媒体库作品时的提示:这类订阅不搜索也不下载(L5) */ export const UNBOUND_HINT = "未关联作品,不会自动搜索和下载"; export function SlotBadges({ slots, max = 4, unbound }: { slots: Item["slots"]; max?: number; unbound?: boolean }) { if (unbound) return {UNBOUND_HINT}; if (slots.length === 0) return 没有收录版本; return (
{slots.slice(0, max).map((s) => { const b = slotBadge(s); return ( {s.name} {b.label} ); })} {slots.length > max ? +{slots.length - max} : null}
); } function nextSearchText(x: Item) { if (!x.searchEnabled) return "搜索已关"; if (x.status === "paused" || x.status === "completed") return "—"; return x.nextSearchAt ? relTime(x.nextSearchAt) : "待排期"; } function Meta({ x }: { x: Item }) { return (
上次更新
{relTime(x.lastUpdatedAt)}
下次搜索
{nextSearchText(x)}
站点
s.name).join("、")}> {foldNames(x.sites.map((s) => s.name))}
规则组
s.name).join("、")}> {foldNames( x.ruleGroups.map((s) => s.name), 2, "未设置", )}
); } function TitleLine({ x }: { x: Item }) { const season = seasonLabel(x); return (

{itemTitle(x)}

{[x.year, SUB_KIND_LABEL[x.mediaKind], season].filter(Boolean).join(" · ")}

); } export function SubscriptionCard({ x, onEdit, selected, onSelect, }: { x: Item; onEdit: () => void; selected?: boolean; onSelect?: (v: boolean) => void; }) { return (
{onSelect ? ( onSelect(e.target.checked)} className="h-4 w-4 cursor-pointer accent-foreground" /> ) : null}

{autoFillText(x.autoFill)}

); } /** 列表视图的一行(桌面表格化,手机折成两行) */ export function SubscriptionRow({ x, onEdit, selected, onSelect, }: { x: Item; onEdit: () => void; selected?: boolean; onSelect?: (v: boolean) => void; }) { return (
  • {onSelect ? ( onSelect(e.target.checked)} className="h-4 w-4 cursor-pointer accent-foreground" /> ) : null}
    {relTime(x.lastUpdatedAt)} {nextSearchText(x)}
  • ); }