This repository has been archived on 2026-07-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
cloud-reader/web/src/App.tsx
T
Marko (Hermes Implementer) b29ea8211c feat: customizable mobile reading experience
- Backend: Chapter, ReadingProgress, ReadingSettings models
- Backend: Chapter API (TOC + content), progress tracking, settings CRUD
- Frontend: ReadingPage with chapter navigation
- Frontend: TableOfContents drawer
- Frontend: ReadingSettingsPanel (theme, font, size, orientation)
- Frontend: Custom hooks for settings, chapters, progress tracking
- CSS: Mobile-first reading view with sepia/dark/light/paper themes
- Route: /reader/:bookId reading view from book detail page
- Docs: 001-customizable-mobile-reading-experience.md
2026-05-29 02:55:36 +00:00

61 lines
1.6 KiB
TypeScript

/**
* App — root component with page navigation (library ↔ book detail ↔ reading view).
*/
import { useState, lazy, Suspense } from "react";
import "./App.css";
import "./reader.css";
const LibraryPage = lazy(() => import("./pages/LibraryPage"));
const BookDetailPage = lazy(() => import("./pages/BookDetailPage"));
const ReadingPage = lazy(() => import("./pages/ReadingPage"));
type View =
| { kind: "library" }
| { kind: "detail"; bookId: number }
| { kind: "reader"; book: import("./types").BookDetail };
function LoadingFallback() {
return (
<div className="loading-spinner">
<div className="spinner" />
<p>Loading...</p>
</div>
);
}
export default function App() {
const [view, setView] = useState<View>({ kind: "library" });
const handleBookSelect = (id: number) => {
setView({ kind: "detail", bookId: id });
};
const handleBack = () => {
setView({ kind: "library" });
};
const handleStartReading = (book: import("./types").BookDetail) => {
setView({ kind: "reader", book });
};
return (
<div className="app">
<Suspense fallback={<LoadingFallback />}>
{view.kind === "library" && (
<LibraryPage onBookSelect={handleBookSelect} />
)}
{view.kind === "detail" && view.bookId !== undefined && (
<BookDetailPage
bookId={view.bookId}
onBack={handleBack}
onStartReading={handleStartReading}
/>
)}
{view.kind === "reader" && view.book !== undefined && (
<ReadingPage book={view.book} onBack={handleBack} />
)}
</Suspense>
</div>
);
}