Fix page shift when toggling "offset double spreads" setting

The pages should get shifted to
- the left when enabling
- the right when disabling
the setting.

Currently, the "page push" is inverted.
This can cause the page that is most likely being read to get pushed out of the screen. Which then requires the user to change the page.

Example:

Active page: 1+2
Enable setting: -> 2 (currently) instead of -> 1
This commit is contained in:
schroda
2026-02-21 14:57:00 +01:00
parent ec49cd1ad9
commit 4da2f67b75
8 changed files with 74 additions and 11 deletions

View File

@@ -26,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Library**) Fix total library size chip color in light mode - (**Library**) Fix total library size chip color in light mode
- (**Browse**) Fix missing pinned sources in the source language filter - (**Browse**) Fix missing pinned sources in the source language filter
- (**Browse**) Fix incorrectly showing "local source" source in the source language filter (the local source can't be disabled) - (**Browse**) Fix incorrectly showing "local source" source in the source language filter (the local source can't be disabled)
- (**Reader**) Fix page shift when toggling the "offset double spreads" setting (currently: enable: shift to the right; disable: shift to the left now: inverted)
## [20251230.01] (r2937) - 2025-12-30 ## [20251230.01] (r2937) - 2025-12-30

View File

@@ -306,7 +306,11 @@ export interface ReaderStatePages {
setPageToScrollToIndex: (total: number | null) => void; setPageToScrollToIndex: (total: number | null) => void;
pageUrls: string[]; pageUrls: string[];
setPageUrls: (urls: string[]) => void; setPageUrls: (urls: string[]) => void;
pageLoadStates: { url: string; loaded: boolean; error?: boolean }[]; pageSpreadStates: ReaderPageSpreadState[];
setPageSpreadStates: (
set: ((prevStates: ReaderPageSpreadState[]) => ReaderPageSpreadState[]) | ReaderPageSpreadState[],
) => void;
pageLoadStates: ReaderPageLoadState[];
setPageLoadStates: ( setPageLoadStates: (
set: ((prevStates: ReaderPageLoadState[]) => ReaderPageLoadState[]) | ReaderPageLoadState[], set: ((prevStates: ReaderPageLoadState[]) => ReaderPageLoadState[]) | ReaderPageLoadState[],
) => void; ) => void;

View File

@@ -149,10 +149,7 @@ export const ReaderHotkeys = ({
ReaderService.updateSetting('shouldStretchPage', !getReaderSettingsStore().shouldStretchPage.value), ReaderService.updateSetting('shouldStretchPage', !getReaderSettingsStore().shouldStretchPage.value),
); );
useHotkeys(hotkeys[ReaderHotkey.OFFSET_SPREAD_PAGES], () => useHotkeys(hotkeys[ReaderHotkey.OFFSET_SPREAD_PAGES], () =>
ReaderService.updateSetting( ReaderService.setOffsetDoubleSpreads(!getReaderSettingsStore().shouldOffsetDoubleSpreads.value),
'shouldOffsetDoubleSpreads',
!getReaderSettingsStore().shouldOffsetDoubleSpreads.value,
),
); );
useHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_MODE], () => { useHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_MODE], () => {
updateSettingCycleThrough( updateSettingCycleThrough(

View File

@@ -43,9 +43,7 @@ const BaseReaderNavBarDesktopQuickSettings = ({ openSettings }: Pick<ReaderNavBa
<ReaderNavBarDesktopOffsetDoubleSpread <ReaderNavBarDesktopOffsetDoubleSpread
readingMode={readingMode.value} readingMode={readingMode.value}
shouldOffsetDoubleSpreads={shouldOffsetDoubleSpreads.value} shouldOffsetDoubleSpreads={shouldOffsetDoubleSpreads.value}
setShouldOffsetDoubleSpreads={(value) => setShouldOffsetDoubleSpreads={(value) => ReaderService.setOffsetDoubleSpreads(value)}
ReaderService.updateSetting('shouldOffsetDoubleSpreads', value)
}
/> />
<ReaderNavBarDesktopPageScale <ReaderNavBarDesktopPageScale
pageScaleMode={pageScaleMode} pageScaleMode={pageScaleMode}

View File

@@ -47,12 +47,14 @@ import { getMetadataKey } from '@/features/metadata/Metadata.utils.ts';
import { DirectionOffset } from '@/base/Base.types.ts'; import { DirectionOffset } from '@/base/Base.types.ts';
import { import {
getReaderChaptersStore, getReaderChaptersStore,
getReaderPagesStore,
getReaderSettingsStore, getReaderSettingsStore,
getReaderStore, getReaderStore,
useReaderSettingsStore, useReaderSettingsStore,
} from '@/features/reader/stores/ReaderStore.ts'; } from '@/features/reader/stores/ReaderStore.ts';
import { ReactRouter } from '@/lib/react-router/ReactRouter.ts'; import { ReactRouter } from '@/lib/react-router/ReactRouter.ts';
import { ReaderChaptersStoreSlice } from '@/features/reader/stores/ReaderChaptersStore.ts'; import { ReaderChaptersStoreSlice } from '@/features/reader/stores/ReaderChaptersStore.ts';
import { getPage } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx';
const DIRECTION_TO_INVERTED: Record<Direction, Direction> = { const DIRECTION_TO_INVERTED: Record<Direction, Direction> = {
ltr: 'rtl', ltr: 'rtl',
@@ -237,6 +239,41 @@ export class ReaderService {
: DIRECTION_TO_INVERTED[direction]; : DIRECTION_TO_INVERTED[direction];
} }
/**
* Updates the setting and updated the current page index accordingly.
*
* In case the current page is a spread page, the page index won't get changed.
*
* Enable:
* Push pages to the left
*
* Page 3+2 -> Page 2+1 - the third page gets pushed to the left, out of the screen
*
* Disable:
* Push pages to the right
*
* Page 3+2 -> Page 4+3 - the second page gets pushed to the right, out of the screen
*/
static setOffsetDoubleSpreads(shouldOffset: boolean): void {
const { pages, currentPageIndex, pageSpreadStates } = getReaderPagesStore();
const { readingMode } = getReaderSettingsStore();
const isDoublePageMode = readingMode.value === ReadingMode.DOUBLE_PAGE;
const isSpreadPage = !!pageSpreadStates[currentPageIndex]?.isSpread;
const updatePageIndex = isDoublePageMode && !isSpreadPage;
if (!updatePageIndex) {
ReaderService.updateSetting('shouldOffsetDoubleSpreads', shouldOffset);
return;
}
const page = getPage(currentPageIndex, pages);
const updatedPageIndex = shouldOffset ? page.primary.index : currentPageIndex;
getReaderPagesStore().setCurrentPageIndex(updatedPageIndex);
ReaderService.updateSetting('shouldOffsetDoubleSpreads', shouldOffset);
}
/** /**
* Writes the change immediately to the cache and sends a mutation in case "commit" is true. * Writes the change immediately to the cache and sends a mutation in case "commit" is true.
*/ */

View File

@@ -22,6 +22,7 @@ export const READER_DEFAULT_PAGES_STATE: Omit<
| 'setPageToScrollToIndex' | 'setPageToScrollToIndex'
| 'setTotalPages' | 'setTotalPages'
| 'setPageUrls' | 'setPageUrls'
| 'setPageSpreadStates'
| 'setPageLoadStates' | 'setPageLoadStates'
| 'setPages' | 'setPages'
| 'setTransitionPageMode' | 'setTransitionPageMode'
@@ -32,6 +33,7 @@ export const READER_DEFAULT_PAGES_STATE: Omit<
currentPageIndex: 0, currentPageIndex: 0,
pageToScrollToIndex: null, pageToScrollToIndex: null,
pageUrls: [], pageUrls: [],
pageSpreadStates: [{ url: '', isSpread: false }],
pageLoadStates: [{ url: '', loaded: false }], pageLoadStates: [{ url: '', loaded: false }],
pages: [ pages: [
{ {
@@ -85,6 +87,19 @@ export const createReaderPagesStoreSlice = <T extends ReaderPagesStoreSlice>(
undefined, undefined,
createActionName('setPageUrls'), createActionName('setPageUrls'),
), ),
setPageSpreadStates: (spreadStates) =>
set(
(draft) => {
if (typeof spreadStates === 'function') {
draft.pages.pageSpreadStates = spreadStates(get().pages.pageSpreadStates);
return;
}
draft.pages.pageSpreadStates = spreadStates;
},
undefined,
createActionName('setPageSpreadStates'),
),
setPageLoadStates: (loadStates) => setPageLoadStates: (loadStates) =>
set( set(
(draft) => { (draft) => {

View File

@@ -50,6 +50,7 @@ const BaseReaderChapterViewer = ({
currentPageIndex, currentPageIndex,
setPages: setContextPages, setPages: setContextPages,
setPageUrls: setContextPageUrls, setPageUrls: setContextPageUrls,
setPageSpreadStates: setContextPageSpreadStates,
setPageLoadStates: setContextPageLoadStates, setPageLoadStates: setContextPageLoadStates,
setTotalPages: setContextTotalPages, setTotalPages: setContextTotalPages,
setCurrentPageIndex: setContextCurrentPageIndex, setCurrentPageIndex: setContextCurrentPageIndex,
@@ -93,6 +94,7 @@ const BaseReaderChapterViewer = ({
| 'currentPageIndex' | 'currentPageIndex'
| 'setPages' | 'setPages'
| 'setPageUrls' | 'setPageUrls'
| 'setPageSpreadStates'
| 'setPageLoadStates' | 'setPageLoadStates'
| 'setTotalPages' | 'setTotalPages'
| 'setCurrentPageIndex' | 'setCurrentPageIndex'
@@ -140,7 +142,7 @@ const BaseReaderChapterViewer = ({
READER_DEFAULT_PAGES_STATE.pageLoadStates, READER_DEFAULT_PAGES_STATE.pageLoadStates,
); );
const [pagesToSpreadState, setPagesToSpreadState] = useState<ReaderPageSpreadState[]>( const [pagesToSpreadState, setPagesToSpreadState] = useState<ReaderPageSpreadState[]>(
pageLoadStates.map(({ url }) => ({ url, isSpread: false })), READER_DEFAULT_PAGES_STATE.pageSpreadStates,
); );
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
@@ -201,7 +203,13 @@ const BaseReaderChapterViewer = ({
() => () =>
createUpdateReaderPageLoadState( createUpdateReaderPageLoadState(
actualPages, actualPages,
setPagesToSpreadState, (value) => {
if (isCurrentChapterRef.current) {
setContextPageSpreadStates(value);
}
setPagesToSpreadState(value);
},
(value) => { (value) => {
if (isCurrentChapterRef.current) { if (isCurrentChapterRef.current) {
setContextPageLoadStates(value); setContextPageLoadStates(value);
@@ -270,7 +278,7 @@ const BaseReaderChapterViewer = ({
(value) => updateState(value, setPages, setContextPages), (value) => updateState(value, setPages, setContextPages),
(value) => updateState(value, setPageUrls, setContextPageUrls), (value) => updateState(value, setPageUrls, setContextPageUrls),
(value) => updateState(value, setPageLoadStates, setContextPageLoadStates), (value) => updateState(value, setPageLoadStates, setContextPageLoadStates),
(value) => updateState(value, setPagesToSpreadState, noOp), (value) => updateState(value, setPagesToSpreadState, setContextPageSpreadStates),
(value) => updateState(value, noOp, setContextCurrentPageIndex), (value) => updateState(value, noOp, setContextCurrentPageIndex),
(value) => { (value) => {
if ((isInitialChapter && !arePagesFetched) || scrollIntoView) { if ((isInitialChapter && !arePagesFetched) || scrollIntoView) {

View File

@@ -82,6 +82,7 @@ const BaseReaderViewer = ({
totalPages, totalPages,
setPages, setPages,
setPageUrls, setPageUrls,
setPageSpreadStates,
setPageLoadStates, setPageLoadStates,
setTotalPages, setTotalPages,
setCurrentPageIndex, setCurrentPageIndex,
@@ -96,6 +97,7 @@ const BaseReaderViewer = ({
totalPages: state.pages.totalPages, totalPages: state.pages.totalPages,
setPages: state.pages.setPages, setPages: state.pages.setPages,
setPageUrls: state.pages.setPageUrls, setPageUrls: state.pages.setPageUrls,
setPageSpreadStates: state.pages.setPageSpreadStates,
setPageLoadStates: state.pages.setPageLoadStates, setPageLoadStates: state.pages.setPageLoadStates,
setTotalPages: state.pages.setTotalPages, setTotalPages: state.pages.setTotalPages,
setCurrentPageIndex: state.pages.setCurrentPageIndex, setCurrentPageIndex: state.pages.setCurrentPageIndex,
@@ -381,6 +383,7 @@ const BaseReaderViewer = ({
imageRefs={imageRefs} imageRefs={imageRefs}
setPages={setPages} setPages={setPages}
setPageUrls={setPageUrls} setPageUrls={setPageUrls}
setPageSpreadStates={setPageSpreadStates}
setPageLoadStates={setPageLoadStates} setPageLoadStates={setPageLoadStates}
setTotalPages={setTotalPages} setTotalPages={setTotalPages}
setCurrentPageIndex={setCurrentPageIndex} setCurrentPageIndex={setCurrentPageIndex}