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:
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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'],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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']> = {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user