feat(web): simplify subscription and manual download flows and sync UHD styles
This commit is contained in:
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 }),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
Reference in new issue
Block a user