231887c3b7
Adds DownloadJob/ExternalSearchResult/SourceInfo contract types + mappers, the downloads + search RTKQ endpoints, and the SearchDownloadPage (search external sources, per-result download states) and DownloadsManagerPage (active/history, progress, retry/cancel, poll-while-active). en/ru i18n. Snapshot also bundles in-progress queue/metadata-editor/storage UI work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
107 lines
3.0 KiB
TypeScript
107 lines
3.0 KiB
TypeScript
import { api } from '../index';
|
|
import {
|
|
toDownloadJob,
|
|
toPage,
|
|
type RawDownloadJob,
|
|
type RawPaged,
|
|
} from '../mappers';
|
|
import type {
|
|
DownloadJob,
|
|
DownloadRequestResult,
|
|
PaginatedResponse,
|
|
} from '../types';
|
|
|
|
interface ListParams {
|
|
status?: DownloadJob['status'];
|
|
/** Only the current user's jobs (backend `mine=true`). */
|
|
mine?: boolean;
|
|
page?: number;
|
|
pageSize?: number;
|
|
}
|
|
|
|
interface RawCreateResponse {
|
|
already_in_library: boolean;
|
|
track_id: string | null;
|
|
job: RawDownloadJob | null;
|
|
}
|
|
|
|
export const downloadsApi = api.injectEndpoints({
|
|
endpoints: (build) => ({
|
|
getDownloads: build.query<
|
|
PaginatedResponse<DownloadJob>,
|
|
ListParams | void
|
|
>({
|
|
query: (params) => {
|
|
const p = params ?? {};
|
|
const size = p.pageSize ?? 50;
|
|
return {
|
|
url: '/downloads',
|
|
params: {
|
|
status: p.status,
|
|
mine: p.mine ? true : undefined,
|
|
limit: size,
|
|
offset: ((p.page ?? 1) - 1) * size,
|
|
},
|
|
};
|
|
},
|
|
transformResponse: (raw: RawPaged<RawDownloadJob>) =>
|
|
toPage(raw, toDownloadJob),
|
|
providesTags: (result) =>
|
|
result
|
|
? [
|
|
...result.items.map(({ id }) => ({
|
|
type: 'Download' as const,
|
|
id,
|
|
})),
|
|
'Download',
|
|
]
|
|
: ['Download'],
|
|
}),
|
|
getDownload: build.query<DownloadJob, string>({
|
|
query: (id) => `/downloads/${id}`,
|
|
transformResponse: (raw: RawDownloadJob) => toDownloadJob(raw),
|
|
providesTags: (_r, _e, id) => [{ type: 'Download', id }],
|
|
}),
|
|
/** Request a download of a discovered item (§A4 "Download to library"). */
|
|
createDownload: build.mutation<
|
|
DownloadRequestResult,
|
|
{ source: string; sourceId: string; query?: string }
|
|
>({
|
|
query: (body) => ({
|
|
url: '/downloads',
|
|
method: 'POST',
|
|
body: {
|
|
source: body.source,
|
|
source_id: body.sourceId,
|
|
query: body.query,
|
|
},
|
|
}),
|
|
transformResponse: (raw: RawCreateResponse): DownloadRequestResult => ({
|
|
alreadyInLibrary: raw.already_in_library,
|
|
trackId: raw.track_id ?? undefined,
|
|
job: raw.job ? toDownloadJob(raw.job) : undefined,
|
|
}),
|
|
// A completed dedup can surface an existing library track; refresh both.
|
|
invalidatesTags: ['Download', 'Track'],
|
|
}),
|
|
cancelDownload: build.mutation<void, string>({
|
|
query: (id) => ({ url: `/downloads/${id}`, method: 'DELETE' }),
|
|
invalidatesTags: ['Download'],
|
|
}),
|
|
retryDownload: build.mutation<DownloadJob, string>({
|
|
query: (id) => ({ url: `/downloads/${id}/retry`, method: 'POST' }),
|
|
transformResponse: (raw: RawDownloadJob) => toDownloadJob(raw),
|
|
invalidatesTags: (_r, _e, id) => [{ type: 'Download', id }, 'Download'],
|
|
}),
|
|
}),
|
|
overrideExisting: false,
|
|
});
|
|
|
|
export const {
|
|
useGetDownloadsQuery,
|
|
useGetDownloadQuery,
|
|
useCreateDownloadMutation,
|
|
useCancelDownloadMutation,
|
|
useRetryDownloadMutation,
|
|
} = downloadsApi;
|