From 8dada8628507a1348e3f77e6d9615f17ca007fea Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Tue, 12 May 2026 13:26:40 +0200 Subject: [PATCH] Fix reader page scaling with applied safe area inset The safe area inset wasn't applied everywhere, which potentially caused the scrollbar to appear while the pages should be fitting the screen fixes #1102 --- CHANGELOG.md | 4 +++ src/features/reader/Reader.types.ts | 1 + .../reader/filters/ReaderRGBAFilter.tsx | 9 ++++--- .../reader/settings/ReaderSettings.utils.tsx | 12 +++++++++ .../reader/viewer/ReaderChapterViewer.tsx | 2 ++ src/features/reader/viewer/ReaderViewer.tsx | 2 ++ .../reader/viewer/components/ReaderPage.tsx | 16 ++++++++---- .../reader/viewer/pager/ReaderPager.utils.tsx | 25 +++++++++++-------- .../viewer/pager/components/BasePager.tsx | 3 +++ 9 files changed, 55 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b681a74b..ac7f0c81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] (Preview) +### Fixed + +- (**Reader**) Fix scrollbar appearing with "fit to widt/height/screen" page scale mode and applied safe area insets + ## [20260509.01] (r3147) - 2026-05-09 ### Fixed diff --git a/src/features/reader/Reader.types.ts b/src/features/reader/Reader.types.ts index 10c0dbea..36443097 100644 --- a/src/features/reader/Reader.types.ts +++ b/src/features/reader/Reader.types.ts @@ -350,6 +350,7 @@ export interface ReaderPagerProps | 'customFilter' | 'shouldStretchPage' | 'readerWidth' + | 'safeAreaInset' >, Pick { onLoad?: (pagesIndex: number, url: string, isPrimary?: boolean) => void; diff --git a/src/features/reader/filters/ReaderRGBAFilter.tsx b/src/features/reader/filters/ReaderRGBAFilter.tsx index 5e84ce2e..d1c832cb 100644 --- a/src/features/reader/filters/ReaderRGBAFilter.tsx +++ b/src/features/reader/filters/ReaderRGBAFilter.tsx @@ -17,6 +17,7 @@ const BaseReaderRGBAFilter = ({ readerNavBarWidth }: Pick state.customFilter.rgba); + const safeAreaInset = useReaderSettingsStore('safeAreaInset'); if (!enabled) { return null; @@ -26,10 +27,10 @@ const BaseReaderRGBAFilter = ({ readerNavBarWidth }: Pick readingMode === ReadingMode.DOUBLE_PAGE; @@ -96,3 +98,13 @@ export const getProgressBarPosition = ( return ProgressBarPosition.BOTTOM; }; + +export const getSafeAreaInsets = ( + safeAreaInset: SafeAreaInset, + direction: Exclude, +): string[] => [ + ...(direction === ScrollDirection.Y && safeAreaInset.top ? ['env(safe-area-inset-top)'] : []), + ...(direction === ScrollDirection.X && safeAreaInset.right ? ['env(safe-area-inset-right)'] : []), + ...(direction === ScrollDirection.Y && safeAreaInset.bottom ? ['env(safe-area-inset-bottom)'] : []), + ...(direction === ScrollDirection.X && safeAreaInset.left ? ['env(safe-area-inset-left)'] : []), +]; diff --git a/src/features/reader/viewer/ReaderChapterViewer.tsx b/src/features/reader/viewer/ReaderChapterViewer.tsx index 8d6ece62..1d30128c 100644 --- a/src/features/reader/viewer/ReaderChapterViewer.tsx +++ b/src/features/reader/viewer/ReaderChapterViewer.tsx @@ -52,6 +52,7 @@ const BaseReaderChapterViewer = ({ retryFailedPagesKeyPrefix, readingMode, readerWidth, + safeAreaInset, pageScaleMode, shouldOffsetDoubleSpreads, readingDirection, @@ -427,6 +428,7 @@ const BaseReaderChapterViewer = ({ customFilter={customFilter} shouldStretchPage={shouldStretchPage} readerWidth={readerWidth} + safeAreaInset={safeAreaInset} readerNavBarWidth={readerNavBarWidth} isPreloadMode={isPreloadMode} resumeMode={resumeMode} diff --git a/src/features/reader/viewer/ReaderViewer.tsx b/src/features/reader/viewer/ReaderViewer.tsx index d71fe6ae..a7fc6bd2 100644 --- a/src/features/reader/viewer/ReaderViewer.tsx +++ b/src/features/reader/viewer/ReaderViewer.tsx @@ -108,6 +108,7 @@ const BaseReaderViewer = ({ shouldStretchPage: state.shouldStretchPage.value, isStaticNav: state.isStaticNav, })); + const safeAreaInset = useReaderSettingsStore('safeAreaInset'); const { resumeMode = ReaderResumeMode.START } = useLocation().state ?? { resumeMode: ReaderResumeMode.START, }; @@ -337,6 +338,7 @@ const BaseReaderViewer = ({ retryFailedPagesKeyPrefix={retryFailedPagesKeyPrefix} readingMode={readingMode} readerWidth={readerWidth} + safeAreaInset={safeAreaInset} pageScaleMode={pageScaleMode} shouldOffsetDoubleSpreads={shouldOffsetDoubleSpreads} readingDirection={readingDirection} diff --git a/src/features/reader/viewer/components/ReaderPage.tsx b/src/features/reader/viewer/components/ReaderPage.tsx index 460724ae..f1a00a8d 100644 --- a/src/features/reader/viewer/components/ReaderPage.tsx +++ b/src/features/reader/viewer/components/ReaderPage.tsx @@ -19,6 +19,8 @@ import { applyStyles } from '@/base/utils/ApplyStyles.ts'; import { MediaQuery } from '@/base/utils/MediaQuery.tsx'; import type { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts'; import { useReaderScrollbarStore } from '@/features/reader/stores/ReaderStore.ts'; +import { ScrollDirection } from '@/base/Base.types.ts'; +import { getSafeAreaInsets } from '@/features/reader/settings/ReaderSettings.utils.tsx'; const getCustomFilterString = (customFilter: ReaderCustomFilter): string => Object.keys(customFilter) @@ -63,6 +65,7 @@ const BaseReaderPage = ({ pageScaleMode, shouldStretchPage, readerWidth, + safeAreaInset, onLoad, onError, setRef, @@ -70,7 +73,10 @@ const BaseReaderPage = ({ isLoaded, ...props }: Omit & - Pick & + Pick< + IReaderSettings, + 'readingMode' | 'customFilter' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth' | 'safeAreaInset' + > & Pick & { pageIndex: number; pagesIndex: number; @@ -115,8 +121,8 @@ const BaseReaderPage = ({ shouldStretchPage, pageScaleMode, readerWidth, - readerNavBarWidth + scrollbar.ySize, - scrollbar.xSize, + [readerNavBarWidth, scrollbar.ySize, ...getSafeAreaInsets(safeAreaInset, ScrollDirection.X)], + [scrollbar.xSize, ...getSafeAreaInsets(safeAreaInset, ScrollDirection.Y)], doublePage, isTabletWidth, ), @@ -132,8 +138,8 @@ const BaseReaderPage = ({ pageScaleMode, doublePage, readerWidth, - readerNavBarWidth + scrollbar.ySize, - scrollbar.xSize, + [readerNavBarWidth, scrollbar.ySize, ...getSafeAreaInsets(safeAreaInset, ScrollDirection.X)], + [scrollbar.xSize, ...getSafeAreaInsets(safeAreaInset, ScrollDirection.Y)], ), filter: getCustomFilterString(customFilter), objectFit: 'contain', diff --git a/src/features/reader/viewer/pager/ReaderPager.utils.tsx b/src/features/reader/viewer/pager/ReaderPager.utils.tsx index 1c71b863..83f9fa3b 100644 --- a/src/features/reader/viewer/pager/ReaderPager.utils.tsx +++ b/src/features/reader/viewer/pager/ReaderPager.utils.tsx @@ -56,25 +56,28 @@ const getPageWidthPercentage = ( return 1; }; +const joinOffsetsForCssCalc = (offsets: (number | string)[], operator: '-' | '+' = '-'): string => + offsets.map((offset) => (typeof offset === 'number' ? `${offset}px` : offset)).join(` ${operator} `); + export const getImagePlaceholderStyling = ( readingMode: IReaderSettings['readingMode'], shouldStretchPage: IReaderSettings['shouldStretchPage'], pageScaleMode: IReaderSettings['pageScaleMode'], readerWidth: IReaderSettings['readerWidth'], - widthOffset: number, - heightOffset: number, + widthOffset: (number | string)[], + heightOffset: (number | string)[], isDoublePage?: boolean, isTabletWidth?: boolean, ): CSSObject => { const OVER_9000 = 9000; - const getMaxWidth = (width: string) => `calc(${width} - ${widthOffset}px)`; + const getMaxWidth = (width: string) => `calc(${width} - ${joinOffsetsForCssCalc(widthOffset)})`; const getDesktopWidth = (width: number, readerWidthValue?: number) => getMaxWidth(`${coerceIn(readerWidthValue ?? width, Math.min(width, readerWidthValue ?? OVER_9000), width)}vw`); const setReaderWidth = getSetReaderWidth(readerWidth, pageScaleMode); const fullWidth = getMaxWidth(`${Math.min(100, setReaderWidth ?? OVER_9000)}vw`); - const fullHeight = `calc(100vh - ${heightOffset}px)`; + const fullHeight = `calc(100vh - ${joinOffsetsForCssCalc(heightOffset)})`; const DEFAULT_SINGLE_PAGE_WIDTH = isTabletWidth ? fullWidth : getDesktopWidth(40, setReaderWidth); const DEFAULT_SINGLE_PAGE_HEIGHT = isTabletWidth ? fullHeight : '85vh'; @@ -151,11 +154,11 @@ const getReaderDimensionStyling = ( readingMode: IReaderSettings['readingMode'], shouldStretchPage: IReaderSettings['shouldStretchPage'], pageScaleMode: IReaderSettings['pageScaleMode'], - widthOffset: number, - heightOffset: number, + widthOffset: (number | string)[], + heightOffset: (number | string)[], ): CSSObject => { - const fullWidth = `calc((100vw - ${widthOffset}px) * ${widthPercentage})`; - const fullHeight = `calc(100vh - ${heightOffset}px)`; + const fullWidth = `calc((100vw - ${joinOffsetsForCssCalc(widthOffset)}) * ${widthPercentage})`; + const fullHeight = `calc(100vh - ${joinOffsetsForCssCalc(heightOffset)})`; switch (pageScaleMode) { case ReaderPageScaleMode.WIDTH: @@ -216,8 +219,8 @@ export const getReaderImageStyling = ( pageScaleMode: IReaderSettings['pageScaleMode'], isDoublePage: boolean, readerWidth: IReaderSettings['readerWidth'], - widthOffset: number, - heightOffset: number, + widthOffset: (number | string)[], + heightOffset: (number | string)[], ): CSSObject => { const widthPercentage = getPageWidthPercentage(pageScaleMode, isDoublePage, readerWidth, true); return getReaderDimensionStyling( @@ -292,6 +295,7 @@ export const createReaderPage = ( pageScaleMode: IReaderSettings['pageScaleMode'], shouldStretchPage: IReaderSettings['shouldStretchPage'], readerWidth: IReaderSettings['readerWidth'], + safeAreaInset: IReaderSettings['safeAreaInset'], readerNavBarWidth: NavbarContextType['readerNavBarWidth'], retryKeyPrefix?: string, position?: 'left' | 'right', @@ -322,6 +326,7 @@ export const createReaderPage = ( pageScaleMode={pageScaleMode} shouldStretchPage={shouldStretchPage} readerWidth={readerWidth} + safeAreaInset={safeAreaInset} readerNavBarWidth={readerNavBarWidth} /> ); diff --git a/src/features/reader/viewer/pager/components/BasePager.tsx b/src/features/reader/viewer/pager/components/BasePager.tsx index 4e23797e..371f867f 100644 --- a/src/features/reader/viewer/pager/components/BasePager.tsx +++ b/src/features/reader/viewer/pager/components/BasePager.tsx @@ -35,6 +35,7 @@ const BaseBasePager = ({ pageScaleMode, shouldStretchPage, readerWidth, + safeAreaInset, readerNavBarWidth, resumeMode, handleAsInitialRender, @@ -52,6 +53,7 @@ const BaseBasePager = ({ pageScaleMode: ReaderPagerProps['pageScaleMode'], shouldStretchPage: ReaderPagerProps['shouldStretchPage'], readerWidth: ReaderPagerProps['readerWidth'], + safeAreaInset: ReaderPagerProps['safeAreaInset'], readerNavBarWidth: ReaderPagerProps['readerNavBarWidth'], ) => ReactNode; slots?: { boxProps?: BoxProps }; @@ -125,6 +127,7 @@ const BaseBasePager = ({ pageScaleMode, shouldStretchPage, readerWidth, + safeAreaInset, readerNavBarWidth, ), )}