Structure "reader" in sub-features
This commit is contained in:
160
src/features/reader/viewer/pager/components/BasePager.tsx
Normal file
160
src/features/reader/viewer/pager/components/BasePager.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { forwardRef, memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import Box, { BoxProps } from '@mui/material/Box';
|
||||
import { getPageIndexesToLoad, isATransitionPageVisible } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
|
||||
import { ReaderStatePages } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.types.ts';
|
||||
import {
|
||||
IReaderSettings,
|
||||
ReaderPagerProps,
|
||||
ReaderResumeMode,
|
||||
ReaderTransitionPageMode,
|
||||
} from '@/features/reader/Reader.types.ts';
|
||||
import { applyStyles } from '@/features/core/utils/ApplyStyles.ts';
|
||||
import { isContinuousReadingMode } from '@/features/reader/settings/ReaderSettings.utils.tsx';
|
||||
|
||||
const getPreviousCurrentPageIndex = (resumeMode: ReaderResumeMode): number =>
|
||||
resumeMode === ReaderResumeMode.END ? Number.MAX_SAFE_INTEGER : -1;
|
||||
|
||||
const BaseBasePager = forwardRef<
|
||||
HTMLDivElement,
|
||||
Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix' | 'isPreloadMode'> &
|
||||
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,
|
||||
imageRefs,
|
||||
createPage,
|
||||
slots,
|
||||
readingMode,
|
||||
imagePreLoadAmount,
|
||||
isCurrentChapter,
|
||||
isPreviousChapter,
|
||||
isNextChapter,
|
||||
customFilter,
|
||||
pageScaleMode,
|
||||
shouldStretchPage,
|
||||
readerWidth,
|
||||
scrollbarXSize,
|
||||
scrollbarYSize,
|
||||
readerNavBarWidth,
|
||||
resumeMode,
|
||||
handleAsInitialRender,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const previousCurrentPageIndex = useRef(getPreviousCurrentPageIndex(resumeMode));
|
||||
|
||||
if (handleAsInitialRender) {
|
||||
previousCurrentPageIndex.current = getPreviousCurrentPageIndex(resumeMode);
|
||||
}
|
||||
|
||||
const pagesIndexesToRender = useMemo(
|
||||
() =>
|
||||
getPageIndexesToLoad(
|
||||
currentPageIndex,
|
||||
pages,
|
||||
previousCurrentPageIndex.current,
|
||||
imagePreLoadAmount,
|
||||
readingMode,
|
||||
isCurrentChapter,
|
||||
isPreviousChapter,
|
||||
isNextChapter,
|
||||
),
|
||||
[
|
||||
currentPageIndex,
|
||||
pages,
|
||||
imagePreLoadAmount,
|
||||
readingMode,
|
||||
isCurrentChapter,
|
||||
isPreviousChapter,
|
||||
isNextChapter,
|
||||
],
|
||||
);
|
||||
useEffect(() => {
|
||||
if (isCurrentChapter) {
|
||||
previousCurrentPageIndex.current = currentPageIndex;
|
||||
}
|
||||
}, [pagesIndexesToRender, isCurrentChapter]);
|
||||
|
||||
const setRef = useCallback(
|
||||
(pagesIndex: number, element: HTMLElement | null) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
imageRefs.current[pagesIndex] = element;
|
||||
},
|
||||
[imageRefs],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={ref}
|
||||
{...slots?.boxProps}
|
||||
sx={[
|
||||
{
|
||||
width: 'fit-content',
|
||||
height: 'fit-content',
|
||||
},
|
||||
...(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),
|
||||
{
|
||||
visibility: 'hidden',
|
||||
width: 0,
|
||||
height: 0,
|
||||
m: 0,
|
||||
p: 0,
|
||||
},
|
||||
),
|
||||
]}
|
||||
>
|
||||
{pages.map((page, pagesIndex) =>
|
||||
createPage(
|
||||
page,
|
||||
pagesIndex,
|
||||
pagesIndexesToRender.includes(pagesIndex),
|
||||
[ReaderTransitionPageMode.NONE, ReaderTransitionPageMode.BOTH].includes(transitionPageMode),
|
||||
setRef,
|
||||
readingMode,
|
||||
customFilter,
|
||||
pageScaleMode,
|
||||
shouldStretchPage,
|
||||
readerWidth,
|
||||
scrollbarXSize,
|
||||
scrollbarYSize,
|
||||
readerNavBarWidth,
|
||||
),
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const BasePager = memo(BaseBasePager);
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Direction, useTheme } from '@mui/material/styles';
|
||||
import { forwardRef, Fragment, memo, useMemo } from 'react';
|
||||
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
|
||||
import {
|
||||
IReaderSettings,
|
||||
ReaderPagerProps,
|
||||
ReaderPageScaleMode,
|
||||
ReadingDirection,
|
||||
} from '@/features/reader/Reader.types.ts';
|
||||
import { applyStyles } from '@/features/core/utils/ApplyStyles.ts';
|
||||
import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
|
||||
import { getNextIndexFromPage, getPage } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx';
|
||||
|
||||
const getPagePosition = (
|
||||
pageType: 'first' | 'second',
|
||||
themeDirection: Direction,
|
||||
readingDirection: ReadingDirection,
|
||||
): 'left' | 'right' => {
|
||||
const isLtrReadingDirection = readingDirection === ReadingDirection.LTR;
|
||||
|
||||
if (pageType === 'first') {
|
||||
if (themeDirection === 'ltr') {
|
||||
return isLtrReadingDirection ? 'right' : 'left';
|
||||
}
|
||||
|
||||
return isLtrReadingDirection ? 'left' : 'right';
|
||||
}
|
||||
|
||||
if (themeDirection === 'ltr') {
|
||||
return isLtrReadingDirection ? 'left' : 'right';
|
||||
}
|
||||
|
||||
return isLtrReadingDirection ? 'right' : 'left';
|
||||
};
|
||||
|
||||
const BaseReaderDoublePagedPager = forwardRef<
|
||||
HTMLDivElement,
|
||||
ReaderPagerProps & Pick<IReaderSettings, 'readingDirection' | 'pageScaleMode'>
|
||||
>(({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => {
|
||||
const { currentPageIndex, pages, totalPages, readingDirection, pageScaleMode } = props;
|
||||
|
||||
const { direction: themeDirection } = useTheme();
|
||||
|
||||
const currentPage = useMemo(() => getPage(currentPageIndex, pages), [currentPageIndex, pages]);
|
||||
const isLtrReadingDirection = readingDirection === ReadingDirection.LTR;
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) => {
|
||||
const { primary, secondary } = page;
|
||||
|
||||
const currentSecondaryPageIndex = getNextIndexFromPage(currentPage);
|
||||
|
||||
const hasSecondaryPage = !!secondary;
|
||||
const isPrimaryPage = currentPage.primary.index === primary.index;
|
||||
const isSecondaryPage = !!secondary && currentSecondaryPageIndex === secondary.index;
|
||||
|
||||
return (
|
||||
<Fragment key={`${primary.url}_${secondary?.url}`}>
|
||||
{createReaderPage(
|
||||
page,
|
||||
pagesIndex,
|
||||
true,
|
||||
pageLoadStates[primary.index].loaded,
|
||||
isPreloadMode,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
shouldDisplay && isPrimaryPage && shouldLoad,
|
||||
currentPage.primary.index,
|
||||
totalPages,
|
||||
...baseProps,
|
||||
pageLoadStates[primary.index].error ? retryFailedPagesKeyPrefix : undefined,
|
||||
hasSecondaryPage ? getPagePosition('first', themeDirection, readingDirection) : undefined,
|
||||
hasSecondaryPage,
|
||||
)}
|
||||
{hasSecondaryPage &&
|
||||
createReaderPage(
|
||||
{ ...page, primary: { ...page.secondary! } },
|
||||
pagesIndex,
|
||||
false,
|
||||
pageLoadStates[secondary.index].loaded,
|
||||
isPreloadMode,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
shouldDisplay && isSecondaryPage && shouldLoad,
|
||||
currentSecondaryPageIndex,
|
||||
totalPages,
|
||||
...baseProps,
|
||||
pageLoadStates[secondary.index].error ? retryFailedPagesKeyPrefix : undefined,
|
||||
getPagePosition('second', themeDirection, readingDirection),
|
||||
true,
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}}
|
||||
slots={{
|
||||
boxProps: {
|
||||
sx: {
|
||||
...applyStyles(pageScaleMode === ReaderPageScaleMode.ORIGINAL, {
|
||||
margin: 'auto',
|
||||
}),
|
||||
width: '100%',
|
||||
minWidth: 'fit-content',
|
||||
height: '100%',
|
||||
minHeight: 'fit-content',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'nowrap',
|
||||
...applyStyles(themeDirection === 'ltr', {
|
||||
flexDirection: isLtrReadingDirection ? 'row' : 'row-reverse',
|
||||
}),
|
||||
...applyStyles(themeDirection === 'rtl', {
|
||||
flexDirection: isLtrReadingDirection ? 'row-reverse' : 'row',
|
||||
}),
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const ReaderDoublePagedPager = memo(BaseReaderDoublePagedPager);
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { forwardRef, memo } from 'react';
|
||||
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
|
||||
import { applyStyles } from '@/features/core/utils/ApplyStyles.ts';
|
||||
import { IReaderSettings, ReaderPagerProps, ReadingDirection } from '@/features/reader/Reader.types.ts';
|
||||
import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
|
||||
|
||||
const BaseReaderHorizontalPager = forwardRef<
|
||||
HTMLDivElement,
|
||||
ReaderPagerProps & Pick<IReaderSettings, 'pageGap' | 'readingDirection'>
|
||||
>(({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => {
|
||||
const { currentPageIndex, totalPages, pageGap, readingDirection } = props;
|
||||
|
||||
const { direction: themeDirection } = useTheme();
|
||||
|
||||
const isLtrReadingDirection = readingDirection === ReadingDirection.LTR;
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) =>
|
||||
createReaderPage(
|
||||
page,
|
||||
pagesIndex,
|
||||
true,
|
||||
pageLoadStates[page.primary.index].loaded,
|
||||
isPreloadMode,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
!isPreloadMode,
|
||||
currentPageIndex,
|
||||
totalPages,
|
||||
...baseProps,
|
||||
pageLoadStates[page.primary.index].error ? retryFailedPagesKeyPrefix : undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
setRef,
|
||||
)
|
||||
}
|
||||
slots={{
|
||||
boxProps: {
|
||||
sx: {
|
||||
my: 'auto',
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
alignItems: 'center',
|
||||
gap: `${pageGap}px`,
|
||||
...applyStyles(themeDirection === 'ltr', {
|
||||
flexDirection: isLtrReadingDirection ? 'row' : 'row-reverse',
|
||||
justifyContent: isLtrReadingDirection ? 'flex-start' : 'flex-end',
|
||||
}),
|
||||
...applyStyles(themeDirection === 'rtl', {
|
||||
flexDirection: isLtrReadingDirection ? 'row-reverse' : 'row',
|
||||
justifyContent: isLtrReadingDirection ? 'flex-end' : 'flex-start',
|
||||
}),
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const ReaderHorizontalPager = memo(BaseReaderHorizontalPager);
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { forwardRef, memo } from 'react';
|
||||
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
|
||||
import { ReaderPagerProps } from '@/features/reader/Reader.types.ts';
|
||||
import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
|
||||
|
||||
const BaseReaderPagedPager = forwardRef<HTMLDivElement, ReaderPagerProps>(
|
||||
({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => {
|
||||
const { currentPageIndex, totalPages } = props;
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, shouldDisplay, _setRef, ...baseProps) =>
|
||||
createReaderPage(
|
||||
page,
|
||||
pagesIndex,
|
||||
true,
|
||||
pageLoadStates[page.primary.index].loaded,
|
||||
isPreloadMode,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
shouldDisplay && shouldLoad && currentPageIndex === page.primary.index,
|
||||
currentPageIndex,
|
||||
totalPages,
|
||||
...baseProps,
|
||||
pageLoadStates[page.primary.index].error ? retryFailedPagesKeyPrefix : undefined,
|
||||
)
|
||||
}
|
||||
slots={{
|
||||
boxProps: {
|
||||
sx: {
|
||||
margin: 'auto',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const ReaderPagedPager = memo(BaseReaderPagedPager);
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { forwardRef, memo } from 'react';
|
||||
import { BasePager } from '@/features/reader/viewer/pager/components/BasePager.tsx';
|
||||
import { ReaderPagerProps, ReadingMode } from '@/features/reader/Reader.types.ts';
|
||||
import { createReaderPage } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
|
||||
|
||||
const BaseReaderVerticalPager = forwardRef<HTMLDivElement, ReaderPagerProps>(
|
||||
({ onLoad, onError, pageLoadStates, retryFailedPagesKeyPrefix, isPreloadMode, ...props }, ref) => {
|
||||
const { currentPageIndex, totalPages, readingMode, pageGap } = props;
|
||||
|
||||
const isWebtoonMode = readingMode === ReadingMode.WEBTOON;
|
||||
const actualPageGap = isWebtoonMode ? 0 : pageGap;
|
||||
|
||||
return (
|
||||
<BasePager
|
||||
ref={ref}
|
||||
{...props}
|
||||
createPage={(page, pagesIndex, shouldLoad, _, setRef, ...baseProps) =>
|
||||
createReaderPage(
|
||||
page,
|
||||
pagesIndex,
|
||||
true,
|
||||
pageLoadStates[page.primary.index].loaded,
|
||||
isPreloadMode,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
!isPreloadMode,
|
||||
currentPageIndex,
|
||||
totalPages,
|
||||
...baseProps,
|
||||
pageLoadStates[page.primary.index].error ? retryFailedPagesKeyPrefix : undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
page.primary.index !== 0 ? actualPageGap : 0,
|
||||
setRef,
|
||||
)
|
||||
}
|
||||
slots={{ boxProps: { sx: { margin: 'auto' } } }}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const ReaderVerticalPager = memo(BaseReaderVerticalPager);
|
||||
Reference in New Issue
Block a user