Improve memoization of page rendering
This commit is contained in:
@@ -6,10 +6,15 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { ComponentProps, ForwardedRef, forwardRef, memo } from 'react';
|
||||
import { ComponentProps, memo, useCallback } from 'react';
|
||||
import { SpinnerImage } from '@/modules/core/components/SpinnerImage.tsx';
|
||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||
import { IReaderSettings, ReaderCustomFilter, TReaderScrollbarContext } from '@/modules/reader/types/Reader.types.ts';
|
||||
import {
|
||||
IReaderSettings,
|
||||
ReaderCustomFilter,
|
||||
ReaderPagerProps,
|
||||
TReaderScrollbarContext,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
import {
|
||||
getImageMarginStyling,
|
||||
getImagePlaceholderStyling,
|
||||
@@ -49,9 +54,10 @@ const getCustomFilterString = (customFilter: ReaderCustomFilter): string =>
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
const BaseReaderPage = forwardRef(
|
||||
(
|
||||
{
|
||||
const BaseReaderPage = ({
|
||||
pageIndex,
|
||||
pagesIndex,
|
||||
isPrimaryPage,
|
||||
display,
|
||||
doublePage = false,
|
||||
position,
|
||||
@@ -64,22 +70,30 @@ const BaseReaderPage = forwardRef(
|
||||
readerWidth,
|
||||
scrollbarXSize,
|
||||
scrollbarYSize,
|
||||
onLoad,
|
||||
onError,
|
||||
setRef,
|
||||
...props
|
||||
}: Omit<ComponentProps<typeof SpinnerImage>, 'ref' | 'spinnerStyle' | 'imgStyle'> &
|
||||
Pick<
|
||||
IReaderSettings,
|
||||
'readingMode' | 'customFilter' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth'
|
||||
> &
|
||||
}: Omit<ComponentProps<typeof SpinnerImage>, 'ref' | 'spinnerStyle' | 'imgStyle' | 'onLoad' | 'onError'> &
|
||||
Pick<IReaderSettings, 'readingMode' | 'customFilter' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth'> &
|
||||
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> & {
|
||||
pageIndex: number;
|
||||
pagesIndex: number;
|
||||
isPrimaryPage: boolean;
|
||||
display: boolean;
|
||||
doublePage?: boolean;
|
||||
position?: 'left' | 'right';
|
||||
marginTop?: number;
|
||||
},
|
||||
ref: ForwardedRef<HTMLImageElement | null>,
|
||||
) => {
|
||||
onLoad: ReaderPagerProps['onLoad'];
|
||||
onError: ReaderPagerProps['onError'];
|
||||
setRef?: (pagesIndex: number, ref: HTMLElement | null) => void;
|
||||
}) => {
|
||||
const isTabletWidth = MediaQuery.useIsTabletWidth();
|
||||
|
||||
const handleLoad = useCallback(() => onLoad?.(pagesIndex, isPrimaryPage), [onLoad, pagesIndex, isPrimaryPage]);
|
||||
const handleError = useCallback(() => onError?.(pageIndex), [onError, pageIndex]);
|
||||
const updateRef = useCallback((element: HTMLElement | null) => setRef?.(pagesIndex, element), [pagesIndex, setRef]);
|
||||
|
||||
if (!display && !shouldLoad) {
|
||||
return null;
|
||||
}
|
||||
@@ -87,9 +101,11 @@ const BaseReaderPage = forwardRef(
|
||||
return (
|
||||
<SpinnerImage
|
||||
{...props}
|
||||
onLoad={handleLoad}
|
||||
onError={handleError}
|
||||
shouldLoad={shouldLoad}
|
||||
shouldDecode
|
||||
ref={ref}
|
||||
ref={updateRef}
|
||||
spinnerStyle={{
|
||||
backgroundColor: 'background.paper',
|
||||
...getImagePlaceholderStyling(
|
||||
@@ -108,14 +124,7 @@ const BaseReaderPage = forwardRef(
|
||||
...getImageMarginStyling(doublePage, position),
|
||||
}}
|
||||
imgStyle={{
|
||||
...getImageWidthStyling(
|
||||
readingMode,
|
||||
shouldStretchPage,
|
||||
pageScaleMode,
|
||||
doublePage,
|
||||
readerWidth,
|
||||
true,
|
||||
),
|
||||
...getImageWidthStyling(readingMode, shouldStretchPage, pageScaleMode, doublePage, readerWidth, true),
|
||||
display: 'block',
|
||||
...applyStyles(!display, {
|
||||
display: 'none',
|
||||
@@ -138,8 +147,7 @@ const BaseReaderPage = forwardRef(
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const ReaderPage = withPropsFrom(
|
||||
memo(BaseReaderPage),
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
ForwardedRef,
|
||||
forwardRef,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useLayoutEffect,
|
||||
@@ -162,6 +163,30 @@ const BaseReaderViewer = forwardRef(
|
||||
|
||||
const inViewportType = READING_MODE_TO_IN_VIEWPORT_TYPE[readingMode];
|
||||
|
||||
const onLoad = useCallback(
|
||||
(pagesIndex: number, isPrimary: boolean = true) => {
|
||||
const page = actualPages[pagesIndex];
|
||||
const { index, url } = isPrimary ? page.primary : page.secondary!;
|
||||
|
||||
if (readingMode === ReadingMode.DOUBLE_PAGE) {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
setPagesToSpreadState((prevState) => prevState.toSpliced(index, 1, isSpreadPage(img)));
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
setPageLoadStates((statePageLoadStates) => statePageLoadStates.toSpliced(index, 1, { loaded: true }));
|
||||
},
|
||||
[actualPages, readingMode],
|
||||
);
|
||||
|
||||
const onError = useCallback((pageIndex: number) => {
|
||||
setPageLoadStates((statePageLoadStates) =>
|
||||
statePageLoadStates.toSpliced(pageIndex, 1, { loaded: false, error: true }),
|
||||
);
|
||||
}, []);
|
||||
|
||||
// reset spread state
|
||||
useLayoutEffect(() => {
|
||||
setPagesToSpreadState(Array(totalPages).fill(false));
|
||||
@@ -310,27 +335,8 @@ const BaseReaderViewer = forwardRef(
|
||||
pageLoadStates={pageLoadStates}
|
||||
retryFailedPagesKeyPrefix={retryFailedPagesKeyPrefix}
|
||||
imageRefs={imageRefs}
|
||||
onLoad={(pagesIndex, isPrimary = true) => {
|
||||
const page = actualPages[pagesIndex];
|
||||
const { index, url } = isPrimary ? page.primary : page.secondary!;
|
||||
|
||||
if (readingMode === ReadingMode.DOUBLE_PAGE) {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
setPagesToSpreadState((prevState) => prevState.toSpliced(index, 1, isSpreadPage(img)));
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
setPageLoadStates((statePageLoadStates) =>
|
||||
statePageLoadStates.toSpliced(index, 1, { loaded: true }),
|
||||
);
|
||||
}}
|
||||
onError={(pageIndex) => {
|
||||
setPageLoadStates((statePageLoadStates) =>
|
||||
statePageLoadStates.toSpliced(pageIndex, 1, { loaded: false, error: true }),
|
||||
);
|
||||
}}
|
||||
onLoad={onLoad}
|
||||
onError={onError}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { memo, ReactNode, useEffect, useMemo, useRef } from 'react';
|
||||
import { memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import Box, { BoxProps } from '@mui/material/Box';
|
||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||
import { getImageWidthStyling, getPageIndexesToLoad } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
||||
@@ -37,7 +37,7 @@ const BaseBasePager = ({
|
||||
pagesIndex: number,
|
||||
shouldLoad: boolean,
|
||||
shouldDisplay: boolean,
|
||||
setRef: (element: HTMLElement | null) => void,
|
||||
setRef: (pagesIndex: number, element: HTMLElement | null) => void,
|
||||
) => ReactNode;
|
||||
slots?: { boxProps?: BoxProps };
|
||||
}) => {
|
||||
@@ -50,6 +50,14 @@ const BaseBasePager = ({
|
||||
previousCurrentPageIndex.current = currentPageIndex;
|
||||
}, [pagesIndexesToRender]);
|
||||
|
||||
const setRef = useCallback(
|
||||
(pagesIndex: number, element: HTMLElement | null) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
imageRefs.current[pagesIndex] = element;
|
||||
},
|
||||
[imageRefs],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
{...slots?.boxProps}
|
||||
@@ -70,10 +78,7 @@ const BaseBasePager = ({
|
||||
pagesIndex,
|
||||
pagesIndexesToRender.includes(pagesIndex),
|
||||
[ReaderTransitionPageMode.NONE, ReaderTransitionPageMode.BOTH].includes(transitionPageMode),
|
||||
(element) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
imageRefs.current[pagesIndex] = element;
|
||||
},
|
||||
setRef,
|
||||
),
|
||||
)}
|
||||
<ReaderTransitionPage
|
||||
|
||||
@@ -69,8 +69,10 @@ const BaseReaderDoublePagedPager = ({
|
||||
<Fragment key={`${primary.url}_${secondary?.url}`}>
|
||||
{createReaderPage(
|
||||
page,
|
||||
() => onLoad?.(pagesIndex),
|
||||
() => onError?.(primary.index),
|
||||
pagesIndex,
|
||||
true,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
shouldDisplay && isPrimaryPage,
|
||||
currentPage.primary.index,
|
||||
@@ -82,8 +84,10 @@ const BaseReaderDoublePagedPager = ({
|
||||
{hasSecondaryPage &&
|
||||
createReaderPage(
|
||||
{ ...page, primary: { ...page.secondary! } },
|
||||
() => onLoad?.(pagesIndex, false),
|
||||
() => onError?.(secondary.index),
|
||||
pagesIndex,
|
||||
false,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
shouldDisplay && isSecondaryPage,
|
||||
currentSecondaryPageIndex,
|
||||
|
||||
@@ -36,8 +36,10 @@ const BaseReaderHorizontalPager = ({
|
||||
createPage={(page, pagesIndex, shouldLoad, _, setRef) =>
|
||||
createReaderPage(
|
||||
page,
|
||||
() => onLoad?.(pagesIndex),
|
||||
() => onError?.(page.primary.index),
|
||||
pagesIndex,
|
||||
true,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
true,
|
||||
currentPageIndex,
|
||||
|
||||
@@ -26,8 +26,10 @@ const BaseReaderPagedPager = ({
|
||||
createPage={(page, pagesIndex, shouldLoad, shouldDisplay) =>
|
||||
createReaderPage(
|
||||
page,
|
||||
() => onLoad?.(pagesIndex),
|
||||
() => onError?.(page.primary.index),
|
||||
pagesIndex,
|
||||
true,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
shouldDisplay && currentPageIndex === page.primary.index,
|
||||
currentPageIndex,
|
||||
|
||||
@@ -33,8 +33,10 @@ const BaseReaderVerticalPager = ({
|
||||
createPage={(page, pagesIndex, shouldLoad, _, setRef) =>
|
||||
createReaderPage(
|
||||
page,
|
||||
() => onLoad?.(pagesIndex),
|
||||
() => onError?.(page.primary.index),
|
||||
pagesIndex,
|
||||
true,
|
||||
onLoad,
|
||||
onError,
|
||||
shouldLoad,
|
||||
true,
|
||||
currentPageIndex,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { Direction, Theme } from '@mui/material/styles';
|
||||
import { ReactNode } from 'react';
|
||||
import { ComponentProps, ReactNode } from 'react';
|
||||
import {
|
||||
IReaderSettings,
|
||||
PageInViewportType,
|
||||
@@ -227,7 +227,16 @@ export const createPageData = (url: string, index: number): ReaderStatePages['pa
|
||||
|
||||
export const createPagesData = (pageUrls: string[]): ReaderStatePages['pages'] => pageUrls.map(createPageData);
|
||||
|
||||
const getPageDownloadPriority = (currentPageIndex: number, pageIndex: number, totalPages: number): number => {
|
||||
const getPageDownloadPriority = (
|
||||
currentPageIndex: number,
|
||||
pageIndex: number,
|
||||
totalPages: number,
|
||||
shouldLoad: boolean,
|
||||
): number => {
|
||||
if (!shouldLoad) {
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
const distanceToCurrentPage = Math.abs(pageIndex - currentPageIndex);
|
||||
const priorityBasedOnDistance = totalPages - distanceToCurrentPage;
|
||||
|
||||
@@ -241,8 +250,10 @@ const getPageDownloadPriority = (currentPageIndex: number, pageIndex: number, to
|
||||
|
||||
export const createReaderPage = (
|
||||
{ primary: { index, alt, url } }: ReaderStatePages['pages'][number],
|
||||
onLoad: () => void,
|
||||
onError: (() => void) | undefined,
|
||||
pagesIndex: number,
|
||||
isPrimaryPage: boolean,
|
||||
onLoad: ComponentProps<typeof ReaderPage>['onLoad'],
|
||||
onError: ComponentProps<typeof ReaderPage>['onError'],
|
||||
shouldLoad: boolean,
|
||||
display: boolean,
|
||||
currentPageIndex: number,
|
||||
@@ -251,15 +262,18 @@ export const createReaderPage = (
|
||||
position?: 'left' | 'right',
|
||||
isDoublePage?: boolean,
|
||||
marginTop?: number,
|
||||
setRef?: (ref: HTMLElement | null) => void,
|
||||
setRef?: (pagesIndex: number, ref: HTMLElement | null) => void,
|
||||
): ReactNode => (
|
||||
<ReaderPage
|
||||
ref={setRef}
|
||||
setRef={setRef}
|
||||
pageIndex={index}
|
||||
pagesIndex={pagesIndex}
|
||||
isPrimaryPage={isPrimaryPage}
|
||||
key={url}
|
||||
src={url}
|
||||
alt={alt}
|
||||
display={display}
|
||||
priority={getPageDownloadPriority(currentPageIndex, index, totalPages)}
|
||||
priority={getPageDownloadPriority(currentPageIndex, index, totalPages, shouldLoad)}
|
||||
position={position}
|
||||
onLoad={onLoad}
|
||||
onError={onError}
|
||||
|
||||
Reference in New Issue
Block a user