Add the /search surface (Pages / Chapters / Mangas tabs) backed by per-user page tags and per-page collections: schema (migration 0023), backend endpoints for page tags/collections and tagged-page aggregations (with the OCR text-search param reserved at 501), plus the frontend API clients, library Page-tags tab, collection page sections, page context menu / AddTagsSheet, and reader long-press wiring. Includes the continuous-reader navigation fixes (?page=N handling, chapter-reset timing, back-button pops history) and tag-normalization hardening accumulated on the branch. Bump version 0.60.2 -> 0.62.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
import { request, type Page } from './client';
|
|
|
|
/** Row returned by `GET /v1/collections/:id/pages`. */
|
|
export type CollectionPageItem = {
|
|
page_id: string;
|
|
chapter_id: string;
|
|
manga_id: string;
|
|
page_number: number;
|
|
chapter_number: number;
|
|
chapter_title: string | null;
|
|
manga_title: string;
|
|
storage_key: string;
|
|
added_at: string;
|
|
};
|
|
|
|
export type CollectionPagesPage = {
|
|
items: CollectionPageItem[];
|
|
page: Page;
|
|
};
|
|
|
|
export type ListOptions = { limit?: number; offset?: number };
|
|
|
|
export async function listCollectionPages(
|
|
collectionId: string,
|
|
opts: ListOptions = {}
|
|
): Promise<CollectionPagesPage> {
|
|
const params = new URLSearchParams();
|
|
if (opts.limit != null) params.set('limit', String(opts.limit));
|
|
if (opts.offset != null) params.set('offset', String(opts.offset));
|
|
const qs = params.toString();
|
|
return request<CollectionPagesPage>(
|
|
`/v1/collections/${encodeURIComponent(collectionId)}/pages${qs ? `?${qs}` : ''}`
|
|
);
|
|
}
|
|
|
|
export async function addPageToCollection(
|
|
collectionId: string,
|
|
pageId: string
|
|
): Promise<void> {
|
|
await request<void>(
|
|
`/v1/collections/${encodeURIComponent(collectionId)}/pages`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({ page_id: pageId })
|
|
}
|
|
);
|
|
}
|
|
|
|
export async function removePageFromCollection(
|
|
collectionId: string,
|
|
pageId: string
|
|
): Promise<void> {
|
|
await request<void>(
|
|
`/v1/collections/${encodeURIComponent(collectionId)}/pages/${encodeURIComponent(pageId)}`,
|
|
{ method: 'DELETE' }
|
|
);
|
|
}
|
|
|
|
/** Which of the user's collections currently contain this page. */
|
|
export async function getMyCollectionsContainingPage(
|
|
pageId: string
|
|
): Promise<string[]> {
|
|
const r = await request<{ collection_ids: string[] }>(
|
|
`/v1/pages/${encodeURIComponent(pageId)}/my-collections`
|
|
);
|
|
return r.collection_ids;
|
|
}
|