feat(web): simplify subscription and manual download flows and sync UHD styles

This commit is contained in:
root committed 2026-10-13 14:37:03 +08:00
1 parent 27c0f27434
commit c3c4456913
33 files changed
+1083 -633

No files matched your search

@@ -4,9 +4,7 @@ import {
ArrowDownWideNarrow,
ArrowUp,
ArrowUpNarrowWide,
Bot,
ChevronDown,
Download,
ExternalLink,
PackageSearch,
RefreshCw,
@@ -38,7 +36,7 @@ import {
selectedCount,
useQuickFilters,
} from "./quick-filter-state.ts";
import { delegateToAgent, SearchDownloadDialog } from "./search-download-dialog.tsx";
import { SearchDownloadActions } from "./search-download-dialog.tsx";
import {
gbToBytes,
groupByWork,
@@ -147,7 +145,6 @@ export function ReleaseResults({
error: res.error,
resetKey: JSON.stringify(api),
});
const [dl, setDl] = useState<SearchRelease | null>(null);
const [selected, setSelected] = useState<SearchRelease | null>(null);
const filtered =
selectedCount(quick) > 0 || RELEASE_FILTER_KEYS.some((k) => k !== "sort" && k !== "order" && !!filters[k]);
@@ -267,7 +264,7 @@ export function ReleaseResults({
className="py-12"
/>
) : (
<ReleaseList rows={paged.items ?? []} onOpen={setSelected} onDownload={setDl} />
<ReleaseList rows={paged.items ?? []} onOpen={setSelected} />
)}
{rows.length > 0 || res.hasNextPage ? <Pagination {...paged.pagination} /> : null}
{first?.unmatchedTotal ? (
@@ -276,7 +273,6 @@ export function ReleaseResults({
rows={first.unmatched ?? []}
total={first.unmatchedTotal}
onOpen={setSelected}
onDownload={setDl}
/>
) : null}
</div>
@@ -293,7 +289,6 @@ export function ReleaseResults({
/>
) : null}
{dl ? <SearchDownloadDialog release={dl} open={!!dl} onOpenChange={(o) => !o && setDl(null)} /> : null}
{selected?.sourceReleaseId ? (
<ReleaseDetailSheet
key={selected.key}
@@ -314,13 +309,11 @@ export function UnmatchedReleases({
rows,
total,
onOpen,
onDownload,
defaultOpen = false,
}: {
rows: SearchRelease[];
total: number;
onOpen: (r: SearchRelease) => void;
onDownload: (r: SearchRelease) => void;
defaultOpen?: boolean;
}) {
const [open, setOpen] = useState(defaultOpen);
@@ -338,7 +331,7 @@ export function UnmatchedReleases({
</button>
{open ? (
<div className="border-t border-border/60">
<ReleaseList rows={rows} onOpen={onOpen} onDownload={onDownload} />
<ReleaseList rows={rows} onOpen={onOpen} />
</div>
) : null}
</div>
@@ -517,20 +510,10 @@ function WorkHeader({ g, count }: { g: WorkGroup; count: number }) {
}
/** 按作品分组的结果列表;只涉及一部作品时作品名只在上方出现一次 */
function ReleaseList({
rows,
onOpen,
onDownload,
}: {
rows: SearchRelease[];
onOpen: (r: SearchRelease) => void;
onDownload: (r: SearchRelease) => void;
}) {
function ReleaseList({ rows, onOpen }: { rows: SearchRelease[]; onOpen: (r: SearchRelease) => void }) {
const { grouped, groups } = groupByWork(rows);
const single = !grouped && groups[0]?.entity ? groups[0] : null;
const row = (r: SearchRelease) => (
<ReleaseRow key={r.key} r={r} onOpen={() => onOpen(r)} onDownload={() => onDownload(r)} />
);
const row = (r: SearchRelease) => <ReleaseRow key={r.key} r={r} onOpen={() => onOpen(r)} />;
if (!grouped)
return (
<>
@@ -618,7 +601,7 @@ function VerdictLine({ r }: { r: SearchRelease }) {
* 资源行:左边 = 促销 + 标题 / 规格标签 / 规则判定;右边 = 站点、大小、做种·下载、发布时间定宽对齐,
* 下面是操作。宽屏像表格一样上下对齐,窄屏右栏折到下面。
*/
function ReleaseRow({ r, onOpen, onDownload }: { r: SearchRelease; onOpen: () => void; onDownload: () => void }) {
function ReleaseRow({ r, onOpen }: { r: SearchRelease; onOpen: () => void }) {
const hr = hrBadge(r);
const seedless = r.seeders === 0 || r.seeders === null;
return (
@@ -674,14 +657,7 @@ function ReleaseRow({ r, onOpen, onDownload }: { r: SearchRelease; onOpen: () =>
</RecordLink>
</Button>
) : null}
<Button variant="ghost" size="sm" onClick={() => delegateToAgent(r)}>
<Bot />
交给智能体
</Button>
<Button size="sm" onClick={onDownload}>
<Download />
下载
</Button>
<SearchDownloadActions release={r} />
</span>
</div>
</li>
@@ -1,7 +1,7 @@
import type { SearchRelease } from "@umt/contracts";
import { usePlanFromSearch } from "~/api/search.ts";
import { openAgentDrawer } from "~/components/agent/agent-store.ts";
import { ManualDownloadDialog, type ManualDownloadResource } from "~/components/downloads/manual-download-dialog.tsx";
import { ManualDownloadActions, type ManualDownloadResource } from "~/components/downloads/manual-download-dialog.tsx";
import { kindHintOf } from "~/components/downloads/manual-download-model.ts";
import { promoLabel } from "./search-labels.ts";
@@ -36,21 +36,10 @@ export function searchResource(release: SearchRelease): ManualDownloadResource {
};
}
export function SearchDownloadDialog({
release,
open,
onOpenChange,
}: {
release: SearchRelease;
open: boolean;
onOpenChange: (open: boolean) => void;
}) {
export function SearchDownloadActions({ release }: { release: SearchRelease }) {
const submit = usePlanFromSearch();
return (
<ManualDownloadDialog
key={`${release.siteId}:${release.siteReleaseId}`}
open={open}
onOpenChange={onOpenChange}
<ManualDownloadActions
resource={searchResource(release)}
onDelegate={() => delegateToAgent(release)}
submit={(options) =>
@@ -1,5 +1,6 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { MediaServerInventory, SearchRelease, WorkInventory } from "@umt/contracts";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
@@ -173,9 +174,13 @@ describe("可能不是这部作品的资源", () => {
const render = (defaultOpen: boolean, total = 1) =>
renderToStaticMarkup(
createElement(
MemoryRouter,
null,
createElement(UnmatchedReleases, { rows, total, onOpen: () => {}, onDownload: () => {}, defaultOpen }),
QueryClientProvider,
{ client: new QueryClient() },
createElement(
MemoryRouter,
null,
createElement(UnmatchedReleases, { rows, total, onOpen: () => {}, defaultOpen }),
),
),
);