import { ApiError, request, type Page } from './client'; export type Bookmark = { id: string; user_id: string; manga_id: string; chapter_id: string | null; /** * Reader-facing chapter number, populated by the backend's LEFT * JOIN when listing. `null` for manga-level bookmarks and for * chapter bookmarks whose chapter has been deleted. Absent on the * bare POST response (which returns `Bookmark`, not enriched). */ chapter_number?: number | null; /** * Parent manga title + cover, JOINed in on the list endpoint so * the /bookmarks page can render a real card instead of just a * date. Absent on the POST/DELETE responses. */ manga_title?: string; manga_cover_image_path?: string | null; page: number | null; created_at: string; }; export type BookmarksPage = { items: Bookmark[]; page: Page; }; export type NewBookmark = { manga_id: string; chapter_id?: string | null; page?: number | null; }; export async function createBookmark(input: NewBookmark): Promise { return request('/v1/bookmarks', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(input) }); } export async function deleteBookmark(id: string): Promise { await request(`/v1/bookmarks/${encodeURIComponent(id)}`, { method: 'DELETE' }); } export type ListMyOptions = { limit?: number; offset?: number }; export async function listMyBookmarks( opts: ListMyOptions = {} ): Promise { 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(`/v1/me/bookmarks${qs ? `?${qs}` : ''}`); } /** * Returns the user's bookmarks, or an empty page if they're not * authenticated. Re-throws any non-401 error. */ export async function listMyBookmarksOrEmpty(): Promise { try { return await listMyBookmarks(); } catch (e) { if (e instanceof ApiError && e.status === 401) { return { items: [], page: { limit: 50, offset: 0, total: null } }; } throw e; } }