Add option to disable transition pages

The previous/next transition page of the first/last chapter will still be shown to indicate that there is no previous/next chapter
This commit is contained in:
schroda
2025-05-05 01:26:27 +02:00
parent eb23f74a40
commit f0f740f4af
9 changed files with 99 additions and 11 deletions

View File

@@ -1007,6 +1007,7 @@
"rtl": "Right to left" "rtl": "Right to left"
}, },
"scroll_amount": "Scroll amount", "scroll_amount": "Scroll amount",
"show_transition_page": "Show transition page",
"source_series": "Series settings", "source_series": "Series settings",
"tap_zones": { "tap_zones": {
"edge": "Edge", "edge": "Edge",

View File

@@ -88,6 +88,7 @@ const APP_METADATA_OBJECT: Record<AppMetadataKeys, undefined> = {
extensionLanguages: undefined, extensionLanguages: undefined,
showNsfw: undefined, showNsfw: undefined,
shouldUseInfiniteScroll: undefined, shouldUseInfiniteScroll: undefined,
shouldShowTransitionPage: undefined,
}; };
export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA_OBJECT); export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA_OBJECT);
@@ -164,6 +165,7 @@ export const GLOBAL_METADATA_KEYS: AppMetadataKeys[] = [
'customFilter', 'customFilter',
'shouldSkipDupChapters', 'shouldSkipDupChapters',
'hotkeys', 'hotkeys',
'shouldShowTransitionPage',
// manga // manga
// chapter list options // chapter list options

View File

@@ -102,6 +102,11 @@ export const ReaderBehaviourSettings = ({
checked={settings.shouldUseAutoWebtoonMode} checked={settings.shouldUseAutoWebtoonMode}
onChange={(_, checked) => updateSetting('shouldUseAutoWebtoonMode', checked)} onChange={(_, checked) => updateSetting('shouldUseAutoWebtoonMode', checked)}
/> />
<CheckboxInput
label={t('reader.settings.show_transition_page')}
checked={settings.shouldShowTransitionPage}
onChange={(_, checked) => updateSetting('shouldShowTransitionPage', checked)}
/>
<CheckboxInput <CheckboxInput
label={t('reader.settings.chapter_transition.warning.missing_chapter')} label={t('reader.settings.chapter_transition.warning.missing_chapter')}
checked={settings.shouldInformAboutMissingChapter} checked={settings.shouldInformAboutMissingChapter}

View File

@@ -12,6 +12,7 @@ import { useReaderInfiniteScrollUpdateChapter } from '@/modules/reader/hooks/use
import { IReaderSettings, TReaderScrollbarContext } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, TReaderScrollbarContext } from '@/modules/reader/types/Reader.types.ts';
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts'; import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
import { ChapterIdInfo } from '@/modules/chapter/services/Chapters.ts'; import { ChapterIdInfo } from '@/modules/chapter/services/Chapters.ts';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
const BaseReaderInfiniteScrollUpdateChapter = ({ const BaseReaderInfiniteScrollUpdateChapter = ({
readingMode, readingMode,
@@ -28,7 +29,9 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
scrollbarXSize, scrollbarXSize,
scrollbarYSize, scrollbarYSize,
scrollElement, scrollElement,
}: Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> & shouldShowTransitionPage,
}: Pick<IReaderSettings, 'shouldShowTransitionPage'> &
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> &
Pick<IReaderSettings, 'readingMode' | 'readingDirection' | 'shouldUseInfiniteScroll'> & { Pick<IReaderSettings, 'readingMode' | 'readingDirection' | 'shouldUseInfiniteScroll'> & {
chapterId: ChapterIdInfo['id']; chapterId: ChapterIdInfo['id'];
previousChapterId?: ChapterIdInfo['id']; previousChapterId?: ChapterIdInfo['id'];
@@ -54,6 +57,7 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
scrollbarXSize, scrollbarXSize,
scrollbarYSize, scrollbarYSize,
scrollElement, scrollElement,
shouldShowTransitionPage,
); );
useReaderInfiniteScrollUpdateChapter( useReaderInfiniteScrollUpdateChapter(
'last', 'last',
@@ -69,6 +73,7 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
scrollbarXSize, scrollbarXSize,
scrollbarYSize, scrollbarYSize,
scrollElement, scrollElement,
shouldShowTransitionPage,
); );
return null; return null;
@@ -76,6 +81,6 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
export const ReaderInfiniteScrollUpdateChapter = withPropsFrom( export const ReaderInfiniteScrollUpdateChapter = withPropsFrom(
memo(BaseReaderInfiniteScrollUpdateChapter), memo(BaseReaderInfiniteScrollUpdateChapter),
[() => ({ openChapter: ReaderControls.useOpenChapter() })], [() => ({ openChapter: ReaderControls.useOpenChapter() }), ReaderService.useSettingsWithoutDefaultFlag],
['openChapter'], ['openChapter', 'shouldShowTransitionPage'],
); );

View File

@@ -84,6 +84,7 @@ const BaseReaderTransitionPage = ({
transitionPageMode, transitionPageMode,
readingMode, readingMode,
backgroundColor, backgroundColor,
shouldShowTransitionPage,
manga, manga,
currentChapterName, currentChapterName,
currentChapterScanlator, currentChapterScanlator,
@@ -95,7 +96,7 @@ const BaseReaderTransitionPage = ({
scrollbarYSize, scrollbarYSize,
readerNavBarWidth, readerNavBarWidth,
handleBack, handleBack,
}: Pick<IReaderSettings, 'readingMode' | 'backgroundColor'> & }: Pick<IReaderSettings, 'readingMode' | 'backgroundColor' | 'shouldShowTransitionPage'> &
Pick<TReaderStateMangaContext, 'manga'> & Pick<TReaderStateMangaContext, 'manga'> &
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> & Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> &
Pick<ReaderStatePages, 'transitionPageMode'> & Pick<ReaderStatePages, 'transitionPageMode'> &
@@ -120,6 +121,15 @@ const BaseReaderTransitionPage = ({
const isFirstChapter = !!currentChapterName && !previousChapterName; const isFirstChapter = !!currentChapterName && !previousChapterName;
const isLastChapter = !!currentChapterName && !nextChapterName; const isLastChapter = !!currentChapterName && !nextChapterName;
const forceShowFirstChapterPreviousTransitionPage = isFirstChapter && type === ReaderTransitionPageMode.PREVIOUS;
const forceShowLastChapterNextTransitionPage = isLastChapter && type === ReaderTransitionPageMode.NEXT;
const forceShowTransitionPage =
forceShowFirstChapterPreviousTransitionPage || forceShowLastChapterNextTransitionPage;
if (!shouldShowTransitionPage && !forceShowTransitionPage) {
return null;
}
if (!isTransitionPageVisible(type, transitionPageMode, readingMode)) { if (!isTransitionPageVisible(type, transitionPageMode, readingMode)) {
return null; return null;
} }
@@ -290,5 +300,6 @@ export const ReaderTransitionPage = withPropsFrom(
'transitionPageMode', 'transitionPageMode',
'readingMode', 'readingMode',
'handleBack', 'handleBack',
'shouldShowTransitionPage',
], ],
); );

View File

@@ -72,6 +72,7 @@ const GLOBAL_READER_SETTING_OBJECT: Record<keyof IReaderSettingsGlobal, undefine
shouldInformAboutScanlatorChange: undefined, shouldInformAboutScanlatorChange: undefined,
scrollAmount: undefined, scrollAmount: undefined,
shouldUseInfiniteScroll: undefined, shouldUseInfiniteScroll: undefined,
shouldShowTransitionPage: undefined,
}; };
export const GLOBAL_READER_SETTING_KEYS = Object.keys(GLOBAL_READER_SETTING_OBJECT); export const GLOBAL_READER_SETTING_KEYS = Object.keys(GLOBAL_READER_SETTING_OBJECT);
@@ -157,6 +158,7 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = {
shouldInformAboutScanlatorChange: true, shouldInformAboutScanlatorChange: true,
scrollAmount: ReaderScrollAmount.LARGE, scrollAmount: ReaderScrollAmount.LARGE,
shouldUseInfiniteScroll: true, shouldUseInfiniteScroll: true,
shouldShowTransitionPage: true,
}; };
export const READER_PROGRESS_BAR_POSITION_TO_PLACEMENT: Record<ProgressBarPosition, TooltipProps['placement']> = { export const READER_PROGRESS_BAR_POSITION_TO_PLACEMENT: Record<ProgressBarPosition, TooltipProps['placement']> = {

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { useCallback, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts'; import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
import { ReadingDirection, ReadingMode } from '@/modules/reader/types/Reader.types.ts'; import { ReadingDirection, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
import { import {
@@ -140,13 +140,16 @@ const getElementIntersection = (
}; };
/** /**
* Will change the chapter to the previous or next one depending on the intersection of the first or last page. * Will handle opening the previous or next chapter for infinite scrolling.
* *
* For the initial load of the previous chapter, the intersection of the first page is used, after the initial load * In case the "transition page" is not enabled, the "initial" load of the previous or next chapter is handled
* of the previous chapter the intersection of the last page handles changing the chapter * based on the scroll position.
* After the initial load, the last page of a chapter handles opening the correct chapter.
* *
* @example * In case the "transition page" is enabled, for the initial load of the previous chapter, the intersection of the first
* How it works: * page is used, after the initial load of the previous chapter the intersection of the last page handles changing the chapter.
*
* @example - How the intersection-based approach works:
* |, _ = viewport * |, _ = viewport
* # = page * # = page
* *
@@ -246,12 +249,62 @@ export const useReaderInfiniteScrollUpdateChapter = (
scrollbarXSize: number, scrollbarXSize: number,
scrollbarYSize: number, scrollbarYSize: number,
scrollElement: HTMLElement | null, scrollElement: HTMLElement | null,
shouldShowTransitionPage: boolean,
) => { ) => {
useEffect(() => {
const isContinuousReadingModeActive = isContinuousReadingMode(readingMode);
const isContinuousVerticalReadingModeActive = isContinuousVerticalReadingMode(readingMode);
if (
shouldShowTransitionPage ||
!scrollElement ||
!isContinuousReadingModeActive ||
!shouldUseInfiniteScroll ||
!isCurrentChapter ||
isChapterToOpenVisible ||
chapterToOpenId === undefined
) {
return () => {};
}
const onScroll = () => {
const isAtStartX = scrollElement.scrollLeft === 0;
const isAtEndX = scrollElement.scrollLeft === scrollElement.scrollWidth - scrollElement.clientWidth;
const isAtStartY = scrollElement.scrollTop === 0;
const isAtEndY = scrollElement.scrollTop === scrollElement.scrollHeight - scrollElement.clientHeight;
const isAtStart = isContinuousVerticalReadingModeActive ? isAtStartY : isAtStartX;
const isAtEnd = isContinuousVerticalReadingModeActive ? isAtEndY : isAtEndX;
const loadPreviousChapter = pageType === 'first' && isAtStart;
const loadNextChapter = pageType === 'last' && isAtEnd;
const loadChapter = loadPreviousChapter || loadNextChapter;
if (loadChapter) {
openChapter(chapterToOpenId, undefined, false);
}
};
scrollElement.addEventListener('scroll', onScroll);
return () => scrollElement.removeEventListener('scroll', onScroll);
}, [
readingMode,
scrollElement,
shouldShowTransitionPage,
shouldUseInfiniteScroll,
isCurrentChapter,
isChapterToOpenVisible,
chapterToOpenId,
openChapter,
]);
useIntersectionObserver( useIntersectionObserver(
image, image,
useCallback( useCallback(
(entries) => { (entries) => {
if ( if (
// !shouldShowTransitionPage ||
!shouldUseInfiniteScroll || !shouldUseInfiniteScroll ||
!isContinuousReadingMode(readingMode) || !isContinuousReadingMode(readingMode) ||
chapterToOpenId === undefined chapterToOpenId === undefined
@@ -259,6 +312,10 @@ export const useReaderInfiniteScrollUpdateChapter = (
return; return;
} }
if (!shouldShowTransitionPage && !isChapterToOpenVisible) {
return;
}
const entry = entries[entries.length - 1]; const entry = entries[entries.length - 1];
const elementIntersectionInfo = getElementIntersectionInfo( const elementIntersectionInfo = getElementIntersectionInfo(
@@ -303,6 +360,7 @@ export const useReaderInfiniteScrollUpdateChapter = (
readingMode, readingMode,
readingDirection, readingDirection,
shouldUseInfiniteScroll, shouldUseInfiniteScroll,
shouldShowTransitionPage,
openChapter, openChapter,
scrollbarXSize, scrollbarXSize,
scrollbarYSize, scrollbarYSize,

View File

@@ -359,7 +359,7 @@ export class ReaderControls {
const { previousChapter, nextChapter } = useReaderStateChaptersContext(); const { previousChapter, nextChapter } = useReaderStateChaptersContext();
const { setIsVisible: setIsOverlayVisible } = useReaderOverlayContext(); const { setIsVisible: setIsOverlayVisible } = useReaderOverlayContext();
const { setShowPreview } = useReaderTapZoneContext(); const { setShowPreview } = useReaderTapZoneContext();
const { readingDirection, readingMode } = ReaderService.useSettings(); const { readingDirection, readingMode, shouldShowTransitionPage } = ReaderService.useSettings();
const openChapter = ReaderControls.useOpenChapter(); const openChapter = ReaderControls.useOpenChapter();
const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]); const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]);
@@ -404,9 +404,11 @@ export class ReaderControls {
const areContinuousPagerTransitionPagesVisible = const areContinuousPagerTransitionPagesVisible =
isContinuousReadingModeActive && isATransitionPageVisibleFlag; isContinuousReadingModeActive && isATransitionPageVisibleFlag;
const isPreviousTransitionPageVisible = const isPreviousTransitionPageVisible =
!shouldShowTransitionPage ||
(!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.PREVIOUS) || (!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.PREVIOUS) ||
areContinuousPagerTransitionPagesVisible; areContinuousPagerTransitionPagesVisible;
const isNextTransitionPageVisible = const isNextTransitionPageVisible =
!shouldShowTransitionPage ||
(!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.NEXT) || (!isContinuousReadingModeActive && transitionPageMode === ReaderTransitionPageMode.NEXT) ||
areContinuousPagerTransitionPagesVisible; areContinuousPagerTransitionPagesVisible;
@@ -466,6 +468,7 @@ export class ReaderControls {
openChapter, openChapter,
!!previousChapter, !!previousChapter,
!!nextChapter, !!nextChapter,
shouldShowTransitionPage,
], ],
); );
} }

View File

@@ -161,6 +161,7 @@ export interface IReaderSettingsGlobal {
shouldInformAboutScanlatorChange: boolean; shouldInformAboutScanlatorChange: boolean;
scrollAmount: ReaderScrollAmount; scrollAmount: ReaderScrollAmount;
shouldUseInfiniteScroll: boolean; shouldUseInfiniteScroll: boolean;
shouldShowTransitionPage: boolean;
} }
export interface IReaderSettingsManga { export interface IReaderSettingsManga {