Archived
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
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
# US: Customizable Mobile Reading Experience
|
||||
|
||||
**Issue:** https://gitea-dev.codescripters.org/HermesFactory/cloud-reader/issues (TBD)
|
||||
|
||||
## Overview
|
||||
|
||||
Add a full-screen reading view for ebooks with customizable typography, themes,
|
||||
table of contents navigation, and orientation support. Mobile-first, responsive
|
||||
design that adapts to any screen size.
|
||||
|
||||
---
|
||||
|
||||
## Backend Specification
|
||||
|
||||
### New Models
|
||||
|
||||
#### `apps.books.models.Chapter`
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------------|--------------------|--------------------------------|
|
||||
| id | AutoField (PK) | |
|
||||
| book | FK -> Book | related_name="chapters" |
|
||||
| title | CharField(512) | Chapter title |
|
||||
| number | PositiveIntegerField | Chapter ordering / TOC index |
|
||||
| content | TextField | Chapter text/markdown content |
|
||||
| created_at | DateTimeField | auto_now_add |
|
||||
| updated_at | DateTimeField | auto_now |
|
||||
|
||||
**Constraints:** UniqueConstraint(book, chapter_number)
|
||||
**Ordering:** [book, number]
|
||||
**Index:** FK to book with db_index
|
||||
|
||||
#### `apps.books.models.ReadingProgress`
|
||||
|
||||
| Field | Type | Notes |
|
||||
|------------------|--------------------|--------------------------------|
|
||||
| id | AutoField (PK) | |
|
||||
| user | FK -> User | related_name="reading_progress"|
|
||||
| book | FK -> Book | related_name="reading_progress"|
|
||||
| current_chapter | PositiveIntegerField | Last chapter number |
|
||||
| current_position | PositiveIntegerField | Position within chapter (paragraph) |
|
||||
| percentage | FloatField | 0.0 - 100.0 overall progress |
|
||||
| updated_at | DateTimeField | auto_now |
|
||||
|
||||
**Constraints:** UniqueConstraint(user, book)
|
||||
**Indexes:** (user, book) composite, (user) filter for list queries
|
||||
|
||||
#### `apps.reader.models.ReadingSettings`
|
||||
|
||||
New app `apps/reader/` for reading preferences, isolated from book data model.
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------------------|--------------------|-------------------------------|
|
||||
| id | AutoField (PK) | |
|
||||
| user | OneToOneField -> User | related_name="reading_settings" |
|
||||
| font_family | CharField(32) | "sans-serif", "serif", "monospace" |
|
||||
| font_size | PositiveSmallIntegerField | 12-32, default 18 |
|
||||
| line_height | FloatField | 1.2 - 2.0, default 1.6 |
|
||||
| margin_width | PositiveSmallIntegerField | 8-48, default 16 (px) |
|
||||
| background_color | CharField(7) | Hex color, default "#f5f0eb" |
|
||||
| text_color | CharField(7) | Hex color, default "#1a1a1a" |
|
||||
| brightness | PositiveSmallIntegerField | 0-100, default 100 |
|
||||
| orientation_lock | CharField(16) | "auto", "portrait", "landscape" |
|
||||
| theme | CharField(32) | "sepia", "dark", "light", "paper" |
|
||||
| created_at | DateTimeField | auto_now_add |
|
||||
| updated_at | DateTimeField | auto_now |
|
||||
|
||||
### New API Endpoints
|
||||
|
||||
All under `/api/` prefix, authenticated with JWT.
|
||||
|
||||
#### Reader Settings (`/api/reader/settings/`)
|
||||
|
||||
| Method | URL | Action |
|
||||
|--------|------------------------------|---------------------------|
|
||||
| GET | /api/reader/settings/ | Get current user settings |
|
||||
| PUT | /api/reader/settings/ | Create/update settings |
|
||||
| PATCH | /api/reader/settings/ | Partial update settings |
|
||||
|
||||
- Single-object endpoint (one settings record per user, auto-created on first GET)
|
||||
- Validation: font_size 12-32, line_height 1.2-2.0, margin_width 8-48
|
||||
|
||||
#### Reading Progress (`/api/books/{id}/progress/`)
|
||||
|
||||
| Method | URL | Action |
|
||||
|--------|----------------------------------------|------------------------------|
|
||||
| GET | /api/books/{id}/progress/ | Get reading progress for book|
|
||||
| PUT | /api/books/{id}/progress/ | Create/update reading progress|
|
||||
|
||||
- Nested under book detail
|
||||
- Auto-creates progress record on first PUT
|
||||
|
||||
#### Chapters (`/api/books/{id}/chapters/`)
|
||||
|
||||
| Method | URL | Action |
|
||||
|--------|----------------------------------------|------------------------------|
|
||||
| GET | /api/books/{id}/chapters/ | List chapters for book (TOC) |
|
||||
| GET | /api/books/{id}/chapters/{number}/ | Get specific chapter content |
|
||||
|
||||
- Ordering by `number`
|
||||
- Used by frontend TOC sidebar and content loading
|
||||
|
||||
---
|
||||
|
||||
## Frontend Specification
|
||||
|
||||
### New Pages
|
||||
|
||||
#### `/reader/:bookId` — ReadingPage
|
||||
|
||||
Full-screen reading view with:
|
||||
- Chapter content display (left/right swiping or scroll)
|
||||
- Bottom toolbar: TOC toggle, Settings toggle, Progress indicator
|
||||
- Top bar: Back button, Book title, Chapter title
|
||||
- Swipe/tap/page navigation between chapters
|
||||
|
||||
### New Components
|
||||
|
||||
#### `ReaderToolbar`
|
||||
- Fixed bottom toolbar
|
||||
- TOC button (opens TOC drawer)
|
||||
- Settings/theme button (opens settings panel)
|
||||
- Progress bar showing overall reading progress
|
||||
|
||||
#### `TableOfContents`
|
||||
- Slide-in drawer from left
|
||||
- Lists all chapters with current chapter highlighted
|
||||
- Tap on chapter to navigate
|
||||
- Shows reading progress per chapter
|
||||
|
||||
#### `ReadingSettingsPanel`
|
||||
- Slide-in drawer from right (or bottom sheet on mobile)
|
||||
- Controls:
|
||||
- Theme presets: Sepia, Dark, Light, Paper
|
||||
- Font family: Sans-serif, Serif, Monospace
|
||||
- Font size slider (12-32)
|
||||
- Line height slider (1.2-2.0)
|
||||
- Margin/padding control
|
||||
- Orientation lock toggle (Auto / Portrait / Landscape)
|
||||
- All changes persist immediately via API
|
||||
- LocalStorage fallback when offline
|
||||
|
||||
### New Hooks
|
||||
|
||||
#### `useReadingSettings(bookId)`
|
||||
- Fetches user reading settings from API
|
||||
- Returns current settings + update function
|
||||
- Applies CSS custom properties to document root
|
||||
- Falls back to defaults if API unavailable
|
||||
|
||||
#### `useChapters(bookId)`
|
||||
- Fetches chapter list for TOC
|
||||
- Returns chapters array, current chapter, navigate function
|
||||
- Prefetches next/prev chapter content
|
||||
|
||||
#### `useReadingProgress(bookId)`
|
||||
- Fetches/updates reading progress
|
||||
- Auto-saves position on chapter change and periodic interval
|
||||
|
||||
### New Types
|
||||
|
||||
```typescript
|
||||
interface Chapter {
|
||||
id: number;
|
||||
book: number;
|
||||
title: string;
|
||||
number: number;
|
||||
content?: string; // Only present when fetching individual chapter
|
||||
}
|
||||
|
||||
interface ChapterSummary {
|
||||
id: number;
|
||||
book: number;
|
||||
title: string;
|
||||
number: number;
|
||||
}
|
||||
|
||||
interface ReadingSettings {
|
||||
font_family: "sans-serif" | "serif" | "monospace";
|
||||
font_size: number;
|
||||
line_height: number;
|
||||
margin_width: number;
|
||||
background_color: string;
|
||||
text_color: string;
|
||||
brightness: number;
|
||||
orientation_lock: "auto" | "portrait" | "landscape";
|
||||
theme: "sepia" | "dark" | "light" | "paper";
|
||||
}
|
||||
|
||||
interface ReadingProgress {
|
||||
current_chapter: number;
|
||||
current_position: number;
|
||||
percentage: number;
|
||||
updated_at: string;
|
||||
}
|
||||
```
|
||||
|
||||
### CSS / Theming
|
||||
|
||||
Reading view uses CSS custom properties driven by reading settings:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--reader-bg: var(--bg-color, #f5f0eb);
|
||||
--reader-text: var(--text-color, #1a1a1a);
|
||||
--reader-font-family: var(--font-family, "Georgia", serif);
|
||||
--reader-font-size: var(--font-size, 18px);
|
||||
--reader-line-height: var(--line-height, 1.6);
|
||||
--reader-margin: var(--margin-width, 16px);
|
||||
}
|
||||
```
|
||||
|
||||
Three theme presets:
|
||||
- **Sepia**: `bg:#f5f0eb`, `text:#1a1a1a` — warm, easy on eyes
|
||||
- **Dark**: `bg:#1a1a2e`, `text:#e0e0e0` — for low-light reading
|
||||
- **Light**: `bg:#ffffff`, `text:#1a1a1a` — crisp and clean
|
||||
- **Paper**: `bg:#e8e0d4`, `text:#2c2c2c` — book-like feel
|
||||
|
||||
### Orientation Support
|
||||
|
||||
- CSS `@media (orientation: portrait)` and `@media (orientation: landscape)` breakpoints
|
||||
- Reading settings panel includes orientation lock toggle
|
||||
- On mobile, landscape mode expands content horizontally with wider margins
|
||||
- Portrait mode stacks controls vertically for thumb-reachable UI
|
||||
|
||||
### Routing
|
||||
|
||||
Add to App.tsx:
|
||||
```
|
||||
/ → LibraryPage
|
||||
/books/:bookId → BookDetailPage
|
||||
/reader/:bookId → ReadingPage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Backend models + migrations (Chapter, ReadingProgress, ReadingSettings)
|
||||
2. Backend serializers + views + URLs
|
||||
3. Frontend types + API client
|
||||
4. Frontend hooks (useReadingSettings, useChapters, useReadingProgress)
|
||||
5. Frontend components (ReadingSettingsPanel, TableOfContents, ReaderToolbar)
|
||||
6. Frontend page (ReadingPage)
|
||||
7. Routing updates
|
||||
8. CSS / theming
|
||||
Reference in New Issue
Block a user