Prevent TypeError and incorrect state updates for "pageLoadStates"

Page load state updates of an already closed chapter caused TypeErrors or incorrect state updates.
This commit is contained in:
schroda
2024-12-30 20:51:29 +01:00
parent 55dc9471d9
commit 7c98bab43b
9 changed files with 59 additions and 21 deletions

View File

@@ -104,7 +104,10 @@ const BaseReaderNavBarDesktopActions = memo(
<IconButton
onClick={() => {
setPageLoadStates((statePageLoadStates) =>
statePageLoadStates.map((pageLoadState) => ({ loaded: pageLoadState.loaded })),
statePageLoadStates.map((pageLoadState) => ({
url: pageLoadState.url,
loaded: pageLoadState.loaded,
})),
);
setRetryFailedPagesKeyPrefix(`${pageRetryKeyPrefix.current}`);
pageRetryKeyPrefix.current = (pageRetryKeyPrefix.current + 1) % 1000;

View File

@@ -88,10 +88,15 @@ const BaseReaderPage = ({
onError: ReaderPagerProps['onError'];
setRef?: (pagesIndex: number, ref: HTMLElement | null) => void;
}) => {
const { src } = props;
const isTabletWidth = MediaQuery.useIsTabletWidth();
const handleLoad = useCallback(() => onLoad?.(pagesIndex, isPrimaryPage), [onLoad, pagesIndex, isPrimaryPage]);
const handleError = useCallback(() => onError?.(pageIndex), [onError, pageIndex]);
const handleLoad = useCallback(
() => onLoad?.(pagesIndex, src, isPrimaryPage),
[onLoad, pagesIndex, src, isPrimaryPage],
);
const handleError = useCallback(() => onError?.(pageIndex, src), [onError, pageIndex, src]);
const updateRef = useCallback((element: HTMLElement | null) => setRef?.(pagesIndex, element), [pagesIndex, setRef]);
if (!display && !shouldLoad) {

View File

@@ -28,7 +28,7 @@ import {
TReaderScrollbarContext,
} from '@/modules/reader/types/Reader.types.ts';
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
import { getDoublePageModePages } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { getDoublePageModePages, isPageOfOutdatedPageLoadStates } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
import { ReaderControls } from '@/modules/reader/services/ReaderControls.ts';
@@ -171,10 +171,20 @@ const BaseReaderViewer = forwardRef(
[actualPages, readingMode],
);
const onError = useCallback((pageIndex: number) => {
setPageLoadStates((statePageLoadStates) =>
statePageLoadStates.toSpliced(pageIndex, 1, { loaded: false, error: true }),
);
const onError = useCallback((pageIndex: number, url: string) => {
setPageLoadStates((statePageLoadStates) => {
const pageLoadState = statePageLoadStates[pageIndex];
if (isPageOfOutdatedPageLoadStates(url, pageLoadState)) {
return statePageLoadStates;
}
return statePageLoadStates.toSpliced(pageIndex, 1, {
...pageLoadState,
loaded: false,
error: true,
});
});
}, []);
// reset spread state

View File

@@ -17,7 +17,9 @@ export const ReaderStatePagesContextProvider = ({ children }: { children: ReactN
const [currentPageIndex, setCurrentPageIndex] = useState<ReaderStatePages['currentPageIndex']>(0);
const [pageToScrollToIndex, setPageToScrollToIndex] = useState<ReaderStatePages['pageToScrollToIndex']>(null);
const [pageUrls, setPageUrls] = useState<ReaderStatePages['pageUrls']>([]);
const [pageLoadStates, setPageLoadStates] = useState<ReaderStatePages['pageLoadStates']>([{ loaded: false }]);
const [pageLoadStates, setPageLoadStates] = useState<ReaderStatePages['pageLoadStates']>([
{ url: '', loaded: false },
]);
const [pages, setPages] = useState<ReaderStatePages['pages']>([createPageData('', 0)]);
const [transitionPageMode, setTransitionPageMode] = useState<ReaderStatePages['transitionPageMode']>(
ReaderTransitionPageMode.NONE,

View File

@@ -203,7 +203,7 @@ const BaseReader = ({
setTotalPages(0);
setPages([createPageData('', 0)]);
setPageUrls([]);
setPageLoadStates([{ loaded: false }]);
setPageLoadStates([{ url: '', loaded: false }]);
setIsOverlayVisible(false);
@@ -224,11 +224,13 @@ const BaseReader = ({
newPages.length - 1,
);
const newPageData = createPagesData(newPages);
setArePagesFetched(true);
setTotalPages(pagesPayload.chapter.pageCount);
setPages(createPagesData(newPages));
setPages(newPageData);
setPageUrls(newPages);
setPageLoadStates(newPages.map(() => ({ loaded: false })));
setPageLoadStates(newPageData.map(({ primary: { url } }) => ({ url, loaded: false })));
setCurrentPageIndex(initialReaderPageIndex);
setPageToScrollToIndex(initialReaderPageIndex);
} else {
@@ -238,7 +240,7 @@ const BaseReader = ({
setTotalPages(0);
setPages([createPageData('', 0)]);
setPageUrls([]);
setPageLoadStates([{ loaded: false }]);
setPageLoadStates([{ url: '', loaded: false }]);
}
setTransitionPageMode(ReaderTransitionPageMode.NONE);

View File

@@ -234,8 +234,8 @@ export interface ReaderPagerProps
| 'retryFailedPagesKeyPrefix'
> {
imageRefs: MutableRefObject<(HTMLElement | null)[]>;
onLoad?: (pagesIndex: number, isPrimary?: boolean) => void;
onError?: (pageIndex: number) => void;
onLoad?: (pagesIndex: number, url: string, isPrimary?: boolean) => void;
onError?: (pageIndex: number, url: string) => void;
}
export enum PageInViewportType {

View File

@@ -31,8 +31,8 @@ export interface ReaderStatePages {
setPageToScrollToIndex: React.Dispatch<React.SetStateAction<number | null>>;
pageUrls: string[];
setPageUrls: React.Dispatch<React.SetStateAction<string[]>>;
pageLoadStates: { loaded: boolean; error?: boolean }[];
setPageLoadStates: React.Dispatch<React.SetStateAction<{ loaded: boolean; error?: boolean }[]>>;
pageLoadStates: { url: string; loaded: boolean; error?: boolean }[];
setPageLoadStates: React.Dispatch<React.SetStateAction<{ url: string; loaded: boolean; error?: boolean }[]>>;
pages: PageData[];
setPages: React.Dispatch<React.SetStateAction<PageData[]>>;
transitionPageMode: ReaderTransitionPageMode;

View File

@@ -21,6 +21,7 @@ import {
createPagesData,
getScrollIntoViewInlineOption,
getScrollToXForReadingDirection,
isPageOfOutdatedPageLoadStates,
isSpreadPage,
} from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
@@ -130,9 +131,13 @@ export const createUpdateReaderPageLoadState =
setPageLoadStates: ReaderStatePages['setPageLoadStates'],
readingMode: ReadingMode,
) =>
(pagesIndex: number, isPrimary: boolean = true) => {
(pagesIndex: number, url: string, isPrimary: boolean = true) => {
if (pagesIndex > actualPages.length - 1) {
return;
}
const page = actualPages[pagesIndex];
const { index, url } = isPrimary ? page.primary : page.secondary!;
const { index } = isPrimary ? page.primary : page.secondary!;
if (readingMode === ReadingMode.DOUBLE_PAGE) {
const img = new Image();
@@ -158,11 +163,17 @@ export const createUpdateReaderPageLoadState =
}
setPageLoadStates((statePageLoadStates) => {
if (statePageLoadStates[index].loaded) {
const pageLoadState = statePageLoadStates[index];
if (isPageOfOutdatedPageLoadStates(url, pageLoadState)) {
return statePageLoadStates;
}
return statePageLoadStates.toSpliced(index, 1, { loaded: true });
if (pageLoadState.loaded) {
return statePageLoadStates;
}
return statePageLoadStates.toSpliced(index, 1, { url: pageLoadState.url, loaded: true });
});
};

View File

@@ -524,3 +524,8 @@ export const getScrollToXForReadingDirection = (
return getOptionForDirection(-element.scrollWidth, 0, themeDirectionForReadingDirection);
};
export const isPageOfOutdatedPageLoadStates = (
url: string,
pageLoadState: ReaderStatePages['pageLoadStates'][number] | undefined,
): boolean => pageLoadState === undefined || pageLoadState.url !== url;