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/docs/mobile/010-mobile-reader.md
crisleo94 22ded87250 feat: implement mobile reader
- implement mobile epub reader
2026-06-20 13:33:44 -05:00

6.9 KiB

010 — Mobile EPUB Reader

Status: Implemented Created: 2026-06-04

Objective

Mirror the web reading experience (frontend/src/pages/ReadingPage.tsx and frontend/src/components/reader/*) inside the Expo app so users can open their uploaded library, read EPUBs with persisted progress and typography settings, and manage bookmarks/highlights — reusing the same Django REST API as the web client.

The web EPUB renderer (react-reader / epub.js) is DOM-only, so mobile renders EPUBs with @epubjs-react-native/core, which runs epub.js inside a react-native-webview. This keeps behavior (CFI locations, themes, font controls, TOC, annotations) close to web while staying inside the managed Expo workflow.

Scope

Mirrored from web

  • EPUB rendering with swipe pagination (flow: "paginated").
  • Resume position and debounced progress save (CFI + percentage).
  • Table of contents drawer with jump-to-chapter.
  • Reading settings: theme presets (light/sepia/paper/dark), font family, font size, line spacing — persisted to /api/reader/settings/.
  • Bookmarks: bookmark the current page, list/jump/delete, and create highlights from a text selection. Highlights are re-applied on open (best-effort).

Deferred (not in this pass)

  • PDF reading. PDF books show a placeholder pointing to the web reader.
  • Brightness and orientation-lock controls.
  • Per-highlight color picker (highlights use a single default color).
  • App-wide internationalization (web uses react-i18n-lite).

Architecture

LibraryScreen (EBook list)
        |  navigate("BookDetail", { ebookId })
        v
BookDetailScreen ----------------> ReaderScreen ({ ebookId })
                                        |
                       GET /api/books/ebooks/:id/  (format guard)
                                        |
                       epub? --------------------- pdf? -> placeholder
                          |
                EpubReaderView (ReaderProvider)
                          |
   expo-file-system downloadAsync(file/, Bearer token) -> file:// uri
                          |
   <Reader src=file:// fileSystem=useFileSystem flow="paginated" />
                          |
   onLocationChange -> debounce 800ms -> PATCH progress/
   onSelected       -> POST bookmarks/ (+ highlight annotation)
   useReader().toc  -> goToLocation(href)
   settings change  -> changeTheme / changeFontSize / changeFontFamily
                       + PATCH /api/reader/settings/ (debounced)

The ebook file is downloaded to the app cache with an Authorization header (the /file/ endpoint is JWT-protected) and the local file:// URI is handed to the renderer — mirroring how the web client downloads a blob rather than using a public/signed URL.

Mobile changes

File Role
mobile/src/api/client.ts Adds apiClient, saveTokens, loadTokens, getApiBaseUrl
mobile/src/api/ebooks.ts /api/books/ebooks/ list/detail/toc + getFileUrl(id)
mobile/src/api/reader.ts Reader settings + per-book progress (mirrors web api/reader.ts)
mobile/src/api/annotations.ts Bookmarks CRUD against /api/annotations/bookmarks/
mobile/src/types/reader.ts ReadingSettings, ReadingProgress (full reader shapes)
mobile/src/types/index.ts AppStackParamList, Bookmark, CreateMarkerPayload
mobile/src/hooks/useReadingSettings.ts Loads + debounced-saves reader settings
mobile/src/utils/epubTheme.ts Font stacks, theme palettes, buildEpubTheme()
mobile/src/navigation/AppStack.tsx Native stack: Tabs / BookDetail / Reader
mobile/src/screens/LibraryScreen.tsx Lists user EBooks (ebooksApi.list)
mobile/src/screens/BookDetailScreen.tsx Metadata + start/resume button
mobile/src/screens/ReaderScreen.tsx Format guard: EPUB view vs PDF placeholder
mobile/src/components/reader/EpubReaderView.tsx Reader, progress, bookmarks, settings wiring
mobile/src/components/reader/ReaderToolbar.tsx Title, chapter, progress bar, action buttons
mobile/src/components/reader/TocModal.tsx Table of contents sheet
mobile/src/components/reader/ReadingSettingsModal.tsx Theme/font/size/spacing controls
mobile/src/components/reader/BookmarksModal.tsx Bookmarks & highlights list
mobile/App.tsx Wraps the tree in GestureHandlerRootView

Removed unused scaffolding: mobile/src/navigation/AppNavigator.tsx, mobile/src/navigation/MainTabs.tsx.

API contracts (consumed)

Method Path Purpose
GET /api/books/ebooks/ User library list
GET /api/books/ebooks/:id/ EBook detail (format, progress, cover)
GET /api/books/ebooks/:id/file/ Stream EPUB bytes (JWT, owner)
GET/PATCH /api/books/ebooks/:id/progress/ Reading progress (current_position, last_page, epub_location)
GET/PATCH /api/reader/settings/ Reader typography/theme settings
GET/POST/DELETE /api/annotations/bookmarks/ Bookmarks & highlights (ebook, epub_cfi, chapter_index, ...)

Settings mapping (web -> mobile)

Reader setting Web (epub.js) Mobile (@epubjs-react-native/core)
theme / colors themes.register/select changeTheme(buildEpubTheme()) + defaultTheme
font_size themes.fontSize changeFontSize("Npx")
font_family body font-family changeFontFamily(stack)
line_height body line-height buildEpubTheme() CSS rule
margin_width gap-based padding not applied (deferred)
brightness / orientation_lock applied on web deferred

Dependencies added

  • @epubjs-react-native/core@1.4.7
  • @epubjs-react-native/expo-file-system@1.1.4
  • react-native-webview@13.12.5

(react-native-gesture-handler, react-native-reanimated, and expo-file-system were already present.)

Configuration

Env var Purpose
EXPO_PUBLIC_API_URL Backend base URL (e.g. http://10.0.2.2:8000 on Android emulator, LAN IP on a device)

Compatibility notes

  • The Expo file-system adapter (@epubjs-react-native/expo-file-system) depends only on expo-file-system, so the reader runs in Expo Go. (The library's bare adapter pulls native @dr.pogodin/react-native-fs; that path is not used here.)
  • React 19 / Expo SDK 52 may surface peer-dependency warnings for the @epubjs-react-native/* packages.

Verification

  • Log in; Library lists the user's uploaded EBooks with covers/progress.
  • Open an EPUB; it renders and paginates by swipe.
  • Reopen a book; it resumes at the last position.
  • Change theme/font/size/spacing; the page updates and persists across reopen.
  • Open the TOC and jump to a chapter.
  • Bookmark the current page; it appears in the bookmarks list and can be re-opened/deleted.
  • Select text to create a highlight; it persists and re-renders on reopen.
  • Open a PDF book; the placeholder is shown instead of a crash.