Use "BasePager" for infinite scroll chapter change detection
There can be cases where when scrolling backward/forward the first/last page never gets visible in the viewport due to a large width/height of another page. In that case the intersection observer would never trigger and thus, the chapter wouldn't get changed. By using the Pager, which always takes up the full width/height, this problem will not be possible, since the start/end of the Pager always enters or leaves the viewport.
This commit is contained in:
@@ -149,6 +149,7 @@ const BaseReaderChapterViewer = ({
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const isCurrentChapterRef = useRef(isCurrentChapter);
|
||||
const imageRefs = useRef<(HTMLElement | null)[]>(pages.map(() => null));
|
||||
const pagerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const actualPages = useMemo(() => {
|
||||
const arePagesLoaded = !!totalPages;
|
||||
@@ -404,14 +405,14 @@ const BaseReaderChapterViewer = ({
|
||||
isCurrentChapter={isCurrentChapter}
|
||||
isPreviousChapterVisible={isPreviousChapterVisible}
|
||||
isNextChapterVisible={isNextChapterVisible}
|
||||
firstImage={imageRefs.current[0]}
|
||||
lastImage={imageRefs.current[imageRefs.current.length - 1]}
|
||||
imageWrapper={pagerRef.current}
|
||||
/>
|
||||
{((!isContinuousReadingModeActive && isCurrentChapter) ||
|
||||
(isContinuousReadingModeActive && (isInitialChapter || isLeadingChapter))) && (
|
||||
<ReaderTransitionPage chapterId={chapterId} type={ReaderTransitionPageMode.PREVIOUS} />
|
||||
)}
|
||||
<Pager
|
||||
ref={pagerRef}
|
||||
totalPages={totalPages}
|
||||
currentPageIndex={currentPageIndex}
|
||||
pages={actualPages}
|
||||
|
||||
@@ -22,8 +22,7 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
|
||||
isPreviousChapterVisible,
|
||||
isCurrentChapter,
|
||||
isNextChapterVisible,
|
||||
firstImage,
|
||||
lastImage,
|
||||
imageWrapper,
|
||||
openChapter,
|
||||
}: {
|
||||
readingMode: ReadingMode;
|
||||
@@ -34,8 +33,7 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
|
||||
isPreviousChapterVisible: boolean;
|
||||
isCurrentChapter: boolean;
|
||||
isNextChapterVisible: boolean;
|
||||
firstImage: HTMLElement | null;
|
||||
lastImage: HTMLElement | null;
|
||||
imageWrapper: HTMLElement | null;
|
||||
openChapter: ReturnType<typeof ReaderControls.useOpenChapter>;
|
||||
}) => {
|
||||
useReaderInfiniteScrollUpdateChapter(
|
||||
@@ -47,7 +45,7 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
|
||||
readingMode,
|
||||
readingDirection,
|
||||
openChapter,
|
||||
firstImage,
|
||||
imageWrapper,
|
||||
);
|
||||
useReaderInfiniteScrollUpdateChapter(
|
||||
'last',
|
||||
@@ -58,7 +56,7 @@ const BaseReaderInfiniteScrollUpdateChapter = ({
|
||||
readingMode,
|
||||
readingDirection,
|
||||
openChapter,
|
||||
lastImage,
|
||||
imageWrapper,
|
||||
);
|
||||
|
||||
return null;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { forwardRef, memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import Box, { BoxProps } from '@mui/material/Box';
|
||||
import { getPageIndexesToLoad, isATransitionPageVisible } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
||||
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
|
||||
@@ -14,7 +14,30 @@ import { IReaderSettings, ReaderPagerProps, ReaderTransitionPageMode } from '@/m
|
||||
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
|
||||
import { isContinuousReadingMode } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||
|
||||
const BaseBasePager = ({
|
||||
const BaseBasePager = forwardRef<
|
||||
HTMLDivElement,
|
||||
Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix'> &
|
||||
Pick<IReaderSettings, 'readingMode' | 'imagePreLoadAmount'> & {
|
||||
createPage: (
|
||||
page: ReaderStatePages['pages'][number],
|
||||
pagesIndex: number,
|
||||
shouldLoad: boolean,
|
||||
shouldDisplay: boolean,
|
||||
setRef: (pagesIndex: number, element: HTMLElement | null) => void,
|
||||
readingMode: ReaderPagerProps['readingMode'],
|
||||
customFilter: ReaderPagerProps['customFilter'],
|
||||
pageScaleMode: ReaderPagerProps['pageScaleMode'],
|
||||
shouldStretchPage: ReaderPagerProps['shouldStretchPage'],
|
||||
readerWidth: ReaderPagerProps['readerWidth'],
|
||||
scrollbarXSize: ReaderPagerProps['scrollbarXSize'],
|
||||
scrollbarYSize: ReaderPagerProps['scrollbarYSize'],
|
||||
readerNavBarWidth: ReaderPagerProps['readerNavBarWidth'],
|
||||
) => ReactNode;
|
||||
slots?: { boxProps?: BoxProps };
|
||||
}
|
||||
>(
|
||||
(
|
||||
{
|
||||
currentPageIndex,
|
||||
pages,
|
||||
transitionPageMode,
|
||||
@@ -33,25 +56,9 @@ const BaseBasePager = ({
|
||||
scrollbarXSize,
|
||||
scrollbarYSize,
|
||||
readerNavBarWidth,
|
||||
}: Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix'> &
|
||||
Pick<IReaderSettings, 'readingMode' | 'imagePreLoadAmount'> & {
|
||||
createPage: (
|
||||
page: ReaderStatePages['pages'][number],
|
||||
pagesIndex: number,
|
||||
shouldLoad: boolean,
|
||||
shouldDisplay: boolean,
|
||||
setRef: (pagesIndex: number, element: HTMLElement | null) => void,
|
||||
readingMode: ReaderPagerProps['readingMode'],
|
||||
customFilter: ReaderPagerProps['customFilter'],
|
||||
pageScaleMode: ReaderPagerProps['pageScaleMode'],
|
||||
shouldStretchPage: ReaderPagerProps['shouldStretchPage'],
|
||||
readerWidth: ReaderPagerProps['readerWidth'],
|
||||
scrollbarXSize: ReaderPagerProps['scrollbarXSize'],
|
||||
scrollbarYSize: ReaderPagerProps['scrollbarYSize'],
|
||||
readerNavBarWidth: ReaderPagerProps['readerNavBarWidth'],
|
||||
) => ReactNode;
|
||||
slots?: { boxProps?: BoxProps };
|
||||
}) => {
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const previousCurrentPageIndex = useRef(-1);
|
||||
const pagesIndexesToRender = useMemo(
|
||||
() =>
|
||||
@@ -65,7 +72,15 @@ const BaseBasePager = ({
|
||||
isPreviousChapter,
|
||||
isNextChapter,
|
||||
),
|
||||
[currentPageIndex, pages, imagePreLoadAmount, readingMode, isCurrentChapter, isPreviousChapter, isNextChapter],
|
||||
[
|
||||
currentPageIndex,
|
||||
pages,
|
||||
imagePreLoadAmount,
|
||||
readingMode,
|
||||
isCurrentChapter,
|
||||
isPreviousChapter,
|
||||
isNextChapter,
|
||||
],
|
||||
);
|
||||
useEffect(() => {
|
||||
previousCurrentPageIndex.current = currentPageIndex;
|
||||
@@ -81,6 +96,7 @@ const BaseBasePager = ({
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={ref}
|
||||
{...slots?.boxProps}
|
||||
sx={[
|
||||
{
|
||||
@@ -90,7 +106,8 @@ const BaseBasePager = ({
|
||||
...(Array.isArray(slots?.boxProps?.sx) ? (slots?.boxProps?.sx ?? []) : [slots?.boxProps?.sx]),
|
||||
// hide pager, without actually unmounting it to prevent re-renders, while a chapter transition page is taking up the full screen
|
||||
applyStyles(
|
||||
!isContinuousReadingMode(readingMode) && isATransitionPageVisible(transitionPageMode, readingMode),
|
||||
!isContinuousReadingMode(readingMode) &&
|
||||
isATransitionPageVisible(transitionPageMode, readingMode),
|
||||
{
|
||||
visibility: 'hidden',
|
||||
width: 0,
|
||||
@@ -120,6 +137,7 @@ const BaseBasePager = ({
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const BasePager = memo(BaseBasePager);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { Direction, useTheme } from '@mui/material/styles';
|
||||
import { Fragment, memo, useMemo } from 'react';
|
||||
import { forwardRef, Fragment, memo, useMemo } from 'react';
|
||||
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
|
||||
import {
|
||||
IReaderSettings,
|
||||
@@ -41,13 +41,10 @@ const getPagePosition = (
|
||||
return isLtrReadingDirection ? 'right' : 'left';
|
||||
};
|
||||
|
||||
const BaseReaderDoublePagedPager = ({
|
||||
onLoad,
|
||||
onError,
|
||||
pageLoadStates,
|
||||
retryFailedPagesKeyPrefix,
|
||||
...props
|
||||
}: ReaderPagerProps & Pick<IReaderSettings, 'readingDirection' | 'pageScaleMode'>) => {
|
||||
const BaseReaderDoublePagedPager = forwardRef<
|
||||
HTMLDivElement,
|
||||
ReaderPagerProps & Pick<IReaderSettings, 'readingDirection' | 'pageScaleMode'>
|
||||
>(({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, ...props }, ref) => {
|
||||
const { currentPageIndex, pages, totalPages, readingDirection, pageScaleMode } = props;
|
||||
|
||||
const { direction: themeDirection } = useTheme();
|
||||
@@ -57,6 +54,7 @@ const BaseReaderDoublePagedPager = ({
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) => {
|
||||
const { primary, secondary } = page;
|
||||
@@ -129,6 +127,6 @@ const BaseReaderDoublePagedPager = ({
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export const ReaderDoublePagedPager = memo(BaseReaderDoublePagedPager);
|
||||
|
||||
@@ -7,19 +7,16 @@
|
||||
*/
|
||||
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { memo } from 'react';
|
||||
import { forwardRef, memo } from 'react';
|
||||
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
|
||||
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
|
||||
import { IReaderSettings, ReaderPagerProps, ReadingDirection } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
||||
|
||||
const BaseReaderHorizontalPager = ({
|
||||
onLoad,
|
||||
onError,
|
||||
pageLoadStates,
|
||||
retryFailedPagesKeyPrefix,
|
||||
...props
|
||||
}: ReaderPagerProps & Pick<IReaderSettings, 'pageGap' | 'readingDirection'>) => {
|
||||
const BaseReaderHorizontalPager = forwardRef<
|
||||
HTMLDivElement,
|
||||
ReaderPagerProps & Pick<IReaderSettings, 'pageGap' | 'readingDirection'>
|
||||
>(({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, ...props }, ref) => {
|
||||
const { currentPageIndex, totalPages, pageGap, readingDirection } = props;
|
||||
|
||||
const { direction: themeDirection } = useTheme();
|
||||
@@ -28,6 +25,7 @@ const BaseReaderHorizontalPager = ({
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) =>
|
||||
createReaderPage(
|
||||
@@ -70,6 +68,6 @@ const BaseReaderHorizontalPager = ({
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export const ReaderHorizontalPager = memo(BaseReaderHorizontalPager);
|
||||
|
||||
@@ -6,22 +6,18 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { memo } from 'react';
|
||||
import { forwardRef, memo } from 'react';
|
||||
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
|
||||
import { ReaderPagerProps } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
||||
|
||||
const BaseReaderPagedPager = ({
|
||||
onLoad,
|
||||
onError,
|
||||
pageLoadStates,
|
||||
retryFailedPagesKeyPrefix,
|
||||
...props
|
||||
}: ReaderPagerProps) => {
|
||||
const BaseReaderPagedPager = forwardRef<HTMLDivElement, ReaderPagerProps>(
|
||||
({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, ...props }, ref) => {
|
||||
const { currentPageIndex, totalPages } = props;
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) =>
|
||||
createReaderPage(
|
||||
@@ -48,6 +44,7 @@ const BaseReaderPagedPager = ({
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const ReaderPagedPager = memo(BaseReaderPagedPager);
|
||||
|
||||
@@ -6,18 +6,13 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { memo } from 'react';
|
||||
import { forwardRef, memo } from 'react';
|
||||
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
|
||||
import { ReaderPagerProps, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
||||
|
||||
const BaseReaderVerticalPager = ({
|
||||
onLoad,
|
||||
onError,
|
||||
pageLoadStates,
|
||||
retryFailedPagesKeyPrefix,
|
||||
...props
|
||||
}: ReaderPagerProps) => {
|
||||
const BaseReaderVerticalPager = forwardRef<HTMLDivElement, ReaderPagerProps>(
|
||||
({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, ...props }, ref) => {
|
||||
const { currentPageIndex, totalPages, readingMode, pageGap } = props;
|
||||
|
||||
const isWebtoonMode = readingMode === ReadingMode.WEBTOON;
|
||||
@@ -25,6 +20,7 @@ const BaseReaderVerticalPager = ({
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) =>
|
||||
createReaderPage(
|
||||
@@ -49,6 +45,7 @@ const BaseReaderVerticalPager = ({
|
||||
slots={{ boxProps: { sx: { margin: 'auto' } } }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const ReaderVerticalPager = memo(BaseReaderVerticalPager);
|
||||
|
||||
@@ -195,7 +195,6 @@ export const useReaderInfiniteScrollUpdateChapter = (
|
||||
|
||||
// gets immediately observed once on initial render
|
||||
let isInitialObserve = true;
|
||||
let wasNextChapterOpened = false;
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (isInitialObserve) {
|
||||
@@ -203,10 +202,6 @@ export const useReaderInfiniteScrollUpdateChapter = (
|
||||
return;
|
||||
}
|
||||
|
||||
if (wasNextChapterOpened) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = entries[entries.length - 1];
|
||||
|
||||
const elementIntersectionInfo = getElementIntersectionInfo(
|
||||
@@ -233,16 +228,17 @@ export const useReaderInfiniteScrollUpdateChapter = (
|
||||
const openChapterToOpen = initialOpenPreviousChapter || openNextChapter;
|
||||
if (openChapterToOpen) {
|
||||
openChapter(chapterToOpenId, !isChapterToOpenVisible, false);
|
||||
wasNextChapterOpened = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (openPreviousChapter) {
|
||||
openChapter(chapterId, false, false);
|
||||
wasNextChapterOpened = true;
|
||||
}
|
||||
},
|
||||
{ threshold: [OPEN_CHAPTER_INTERSECTION_RATIO], rootMargin: '-10px 0px 0px 0px' },
|
||||
{
|
||||
threshold: [OPEN_CHAPTER_INTERSECTION_RATIO],
|
||||
rootMargin: pageType === 'first' ? '0px 0px -10px 0px' : '-10px 0px 0px 0px',
|
||||
},
|
||||
);
|
||||
intersectionObserver.observe(image);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { ComponentType, MemoExoticComponent } from 'react';
|
||||
import { ForwardRefExoticComponent, MemoExoticComponent, RefAttributes } from 'react';
|
||||
import {
|
||||
IReaderSettings,
|
||||
IReaderSettingsWithDefaultFlag,
|
||||
@@ -59,7 +59,7 @@ export const isAutoWebtoonMode = (
|
||||
|
||||
export const getPagerForReadingMode = (
|
||||
readingMode: ReadingMode,
|
||||
): MemoExoticComponent<ComponentType<ReaderPagerProps>> => {
|
||||
): MemoExoticComponent<ForwardRefExoticComponent<ReaderPagerProps & RefAttributes<HTMLDivElement>>> => {
|
||||
switch (readingMode) {
|
||||
case ReadingMode.SINGLE_PAGE:
|
||||
return ReaderPagedPager;
|
||||
|
||||
Reference in New Issue
Block a user