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:
Marko (Hermes Implementer)
2026-05-29 02:55:36 +00:00
parent 3b5b301e42
commit b29ea8211c
27 changed files with 2303 additions and 18 deletions
+45 -2
View File
@@ -1,6 +1,6 @@
from rest_framework import serializers
from apps.books.models import Book
from apps.books.models import Book, Chapter, ReadingProgress
class BookSerializer(serializers.ModelSerializer):
@@ -25,4 +25,47 @@ class BookListSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ["id", "title", "author", "total_pages", "cover_image"]
fields = ["id", "title", "author", "total_pages", "cover_image"]
class ChapterSummarySerializer(serializers.ModelSerializer):
"""Compact serializer for TOC listing — no content body."""
class Meta:
model = Chapter
fields = ["id", "book", "title", "number"]
class ChapterDetailSerializer(serializers.ModelSerializer):
"""Full serializer with chapter content for reading view."""
class Meta:
model = Chapter
fields = ["id", "book", "title", "number", "content", "created_at", "updated_at"]
read_only_fields = ["id", "created_at", "updated_at"]
class ReadingProgressSerializer(serializers.ModelSerializer):
"""Serialize reading progress for a book."""
class Meta:
model = ReadingProgress
fields = [
"id",
"book",
"current_chapter",
"current_position",
"percentage",
"updated_at",
]
read_only_fields = ["id", "updated_at"]
def validate_percentage(self, value: float) -> float:
if value < 0.0 or value > 100.0:
raise serializers.ValidationError("Percentage must be between 0 and 100.")
return value
def validate_current_chapter(self, value: int) -> int:
if value < 1:
raise serializers.ValidationError("Chapter number must be positive.")
return value