From 401a1a3daeaf5183fd7719cea308718fb93c7a14 Mon Sep 17 00:00:00 2001 From: Pratyush Kongalla Date: Sun, 8 Jun 2025 17:35:35 +0530 Subject: [PATCH] feat(reader): Implement chapter-limited smooth scroll view This commit introduces a refined "Scroll" page view mode that limits vertical scrolling to the current chapter. This addresses the previous issue where continuous scroll modes would either be choppy or scroll across the entire book. Key changes include: - Configured EPUB.js rendition with 'scrolled' flow and removed the explicit 'continuous' manager to enable chapter-limited scrolling. - Modified keyboard event handling in [Reader.tsx](cci:7://file:///Users/pratyushkongalla/space/other/flow/apps/reader/src/components/Reader.tsx:0:0-0:0) to allow explicit chapter navigation (ArrowLeft/Right) when in scroll mode. - Adjusted wheel event handling in [Reader.tsx](cci:7://file:///Users/pratyushkongalla/space/other/flow/apps/reader/src/components/Reader.tsx:0:0-0:0) to prevent automatic chapter transitions, ensuring users can scroll freely within the current chapter. These changes provide a fluid and responsive scrolling experience while maintaining clear chapter boundaries and user control over navigation. --- apps/reader/locales/en-US.ts | 1 + apps/reader/locales/ja-JP.ts | 1 + apps/reader/locales/zh-CN.ts | 1 + apps/reader/src/components/Reader.tsx | 77 ++++++++++++++----- .../components/viewlets/TypographyView.tsx | 15 ++-- apps/reader/src/state.ts | 9 ++- 6 files changed, 78 insertions(+), 26 deletions(-) diff --git a/apps/reader/locales/en-US.ts b/apps/reader/locales/en-US.ts index f0258b7f..7e288d72 100644 --- a/apps/reader/locales/en-US.ts +++ b/apps/reader/locales/en-US.ts @@ -33,6 +33,7 @@ export default { 'typography.page_view': 'Page View', 'typography.page_view.single_page': 'Single Page', 'typography.page_view.double_page': 'Double Page', + 'typography.page_view.scroll': 'Scroll', 'typography.font_family': 'Font Family', 'typography.font_size': 'Font Size', 'typography.font_weight': 'Font Weight', diff --git a/apps/reader/locales/ja-JP.ts b/apps/reader/locales/ja-JP.ts index 5c099140..2b20c3a3 100644 --- a/apps/reader/locales/ja-JP.ts +++ b/apps/reader/locales/ja-JP.ts @@ -33,6 +33,7 @@ export default { 'typography.page_view': 'ページ表示', 'typography.page_view.single_page': '片ページ', 'typography.page_view.double_page': '見開きページ', + 'typography.page_view.scroll': 'スクロール', 'typography.font_family': 'フォントファミリー', 'typography.font_size': 'フォントサイズ', 'typography.font_weight': 'フォントウェイト', diff --git a/apps/reader/locales/zh-CN.ts b/apps/reader/locales/zh-CN.ts index edfebcba..f13bc752 100644 --- a/apps/reader/locales/zh-CN.ts +++ b/apps/reader/locales/zh-CN.ts @@ -33,6 +33,7 @@ export default { 'typography.page_view': '视图', 'typography.page_view.single_page': '单页', 'typography.page_view.double_page': '双页', + 'typography.page_view.scroll': '滚动', 'typography.font_family': '字体', 'typography.font_size': '字号', 'typography.font_weight': '字重', diff --git a/apps/reader/src/components/Reader.tsx b/apps/reader/src/components/Reader.tsx index 9f34ad71..dc81a142 100644 --- a/apps/reader/src/components/Reader.tsx +++ b/apps/reader/src/components/Reader.tsx @@ -14,8 +14,7 @@ import { useSetRecoilState } from 'recoil' import useTilg from 'tilg' import { useSnapshot } from 'valtio' -import { RenditionSpread } from '@flow/epubjs/types/rendition' -import { navbarState } from '@flow/reader/state' +import { navbarState, PageViewMode } from '@flow/reader/state' import { db } from '../db' import { handleFiles } from '../file' @@ -46,17 +45,34 @@ import * as pages from './pages' function handleKeyDown(tab?: BookTab) { return (e: KeyboardEvent) => { try { - switch (e.code) { - case 'ArrowLeft': - case 'ArrowUp': - tab?.prev() - break - case 'ArrowRight': - case 'ArrowDown': - tab?.next() - break - case 'Space': - e.shiftKey ? tab?.prev() : tab?.next() + const pageViewMode = tab?.book.configuration?.typography?.pageViewMode; + + if (e.code === 'Space') { + e.shiftKey ? tab?.prev() : tab?.next(); + return; + } + + if (pageViewMode === PageViewMode.Scrolled) { + switch (e.code) { + case 'ArrowLeft': + tab?.prev(); + break; + case 'ArrowRight': + tab?.next(); + break; + } + } else { + // Paginated modes + switch (e.code) { + case 'ArrowLeft': + case 'ArrowUp': + tab?.prev(); + break; + case 'ArrowRight': + case 'ArrowDown': + tab?.next(); + break; + } } } catch (error) { // ignore `rendition is undefined` error @@ -224,6 +240,9 @@ function BookPane({ tab, onMouseDown }: BookPaneProps) { // `display: hidden` will lead `rect` to 0 if (size !== 0 && prevSize.current !== 0) { reader.resize() + } else if (size !== 0) { + // initial render + tab.rendition?.resize() } prevSize.current = size }) @@ -259,8 +278,26 @@ function BookPane({ tab, onMouseDown }: BookPaneProps) { * then call {@link updateCustomStyle} to update custom style * according to the latest layout */ - rendition?.spread(typography.spread ?? RenditionSpread.Auto) - }, [typography.spread, rendition]) + switch (typography.pageViewMode) { + case PageViewMode.Scrolled: + rendition?.flow('scrolled') + rendition?.spread('none') // Spread is not applicable in scrolled mode + break + case PageViewMode.SinglePage: + rendition?.flow('paginated') + rendition?.spread('none') + break + case PageViewMode.DoublePage: + rendition?.flow('paginated') + rendition?.spread('auto') + break + case PageViewMode.Auto: + default: + rendition?.flow('auto') + rendition?.spread('auto') + break + } + }, [typography.pageViewMode, rendition]) useEffect(() => applyCustomStyle(), [applyCustomStyle]) @@ -331,10 +368,12 @@ function BookPane({ tab, onMouseDown }: BookPaneProps) { }) useEventListener(iframe, 'wheel', (e) => { - if (e.deltaY < 0) { - tab.prev() - } else { - tab.next() + if (typography.pageViewMode !== PageViewMode.Scrolled) { + if (e.deltaY < 0) { + tab.prev() + } else { + tab.next() + } } }) diff --git a/apps/reader/src/components/viewlets/TypographyView.tsx b/apps/reader/src/components/viewlets/TypographyView.tsx index f7b5995b..2b2215a2 100644 --- a/apps/reader/src/components/viewlets/TypographyView.tsx +++ b/apps/reader/src/components/viewlets/TypographyView.tsx @@ -2,13 +2,13 @@ import clsx from 'clsx' import { useCallback, useRef, useState } from 'react' import { MdAdd, MdRemove } from 'react-icons/md' -import { RenditionSpread } from '@flow/epubjs/types/rendition' import { useTranslation } from '@flow/reader/hooks' import { reader, useReaderSnapshot } from '@flow/reader/models' import { defaultSettings, TypographyConfiguration, useSettings, + PageViewMode, } from '@flow/reader/state' import { keys } from '@flow/reader/utils' @@ -30,7 +30,7 @@ export const TypographyView: React.FC = (props) => { const [localFonts, setLocalFonts] = useState() - const { fontFamily, fontSize, fontWeight, lineHeight, zoom, spread } = + const { fontFamily, fontSize, fontWeight, lineHeight, zoom, pageViewMode } = scope === TypographyScope.Book ? focusedBookTab?.book.configuration?.typography ?? defaultSettings : settings @@ -102,17 +102,20 @@ export const TypographyView: React.FC = (props) => { > (key: string, defaultValue: T): AtomEffect { return ({ setSelf, onSet }) => { if (IS_SERVER) return @@ -36,7 +43,7 @@ export interface TypographyConfiguration { fontWeight?: number fontFamily?: string lineHeight?: number - spread?: RenditionSpread + pageViewMode?: PageViewMode zoom?: number }