Archived
Resolve merge conflicts for PR #25: customizable mobile reading experience
- Backend: Replace PR's Book-only models with main's full models (EBook, BookChapter, DownloadRecord, ReadingProgress, ReadingSettings) - Backend: Add reader app (ReadingSettings model, serializer, view, URL) - Frontend: Port reader feature files from web/ to frontend/ (api, hooks, components/reader/, pages/ReadingPage, types/reader, reader.css) - Frontend: Add /read/:id route to App.tsx for chapter-based reading view - All imports adjusted for frontend/src directory structure
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* API client for the reader module — reading settings, chapters, and progress.
|
||||
*/
|
||||
|
||||
import type {
|
||||
ChapterDetail,
|
||||
ChapterSummary,
|
||||
ReadingProgress,
|
||||
ReadingSettings,
|
||||
} from "../types/reader";
|
||||
|
||||
const API_BASE = "/api";
|
||||
|
||||
/**
|
||||
* Fetch the current user's reading settings.
|
||||
* Auto-creates defaults on the server if none exist.
|
||||
*/
|
||||
export async function getReadingSettings(): Promise<ReadingSettings> {
|
||||
const response = await fetch(`${API_BASE}/reader/settings/`);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch reading settings: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
return response.json() as Promise<ReadingSettings>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update (full or partial) the user's reading settings.
|
||||
*/
|
||||
export async function updateReadingSettings(
|
||||
settings: Partial<ReadingSettings>
|
||||
): Promise<ReadingSettings> {
|
||||
const method = settings.theme !== undefined ? "PUT" : "PATCH";
|
||||
const response = await fetch(`${API_BASE}/reader/settings/`, {
|
||||
method,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(settings),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to update reading settings: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
return response.json() as Promise<ReadingSettings>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the table of contents (chapter list) for a book.
|
||||
*/
|
||||
export async function getChapters(bookId: number): Promise<ChapterSummary[]> {
|
||||
const response = await fetch(`${API_BASE}/books/${bookId}/chapters/`);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch chapters: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
return response.json() as Promise<ChapterSummary[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a specific chapter with full content for reading.
|
||||
*/
|
||||
export async function getChapterContent(
|
||||
bookId: number,
|
||||
chapterNumber: number
|
||||
): Promise<ChapterDetail> {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/books/${bookId}/chapters/${chapterNumber}/`
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch chapter ${chapterNumber}: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
return response.json() as Promise<ChapterDetail>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch reading progress for a book.
|
||||
*/
|
||||
export async function getReadingProgress(
|
||||
bookId: number
|
||||
): Promise<ReadingProgress> {
|
||||
const response = await fetch(`${API_BASE}/books/${bookId}/progress/`);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch reading progress: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
return response.json() as Promise<ReadingProgress>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update reading progress for a book.
|
||||
*/
|
||||
export async function updateReadingProgress(
|
||||
bookId: number,
|
||||
progress: Partial<ReadingProgress>
|
||||
): Promise<ReadingProgress> {
|
||||
const response = await fetch(`${API_BASE}/books/${bookId}/progress/`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(progress),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to update reading progress: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
return response.json() as Promise<ReadingProgress>;
|
||||
}
|
||||
Reference in New Issue
Block a user