2024-12-03 20:24:55 +01:00
|
|
|
/*
|
|
|
|
|
* 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 Box from '@mui/material/Box';
|
2024-12-21 03:46:55 +01:00
|
|
|
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
2024-12-03 20:24:55 +01:00
|
|
|
import { useLocation, useParams } from 'react-router-dom';
|
|
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
|
import {
|
|
|
|
|
getReaderSettings,
|
|
|
|
|
getReaderSettingsFor,
|
|
|
|
|
useDefaultReaderSettings,
|
|
|
|
|
} from '@/modules/reader/services/ReaderSettingsMetadata.ts';
|
|
|
|
|
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
|
|
|
|
import { ReaderOverlay } from '@/modules/reader/components/overlay/ReaderOverlay.tsx';
|
|
|
|
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
|
|
|
|
import { GetChaptersReaderQuery, GetMangaReaderQuery } from '@/lib/graphql/generated/graphql.ts';
|
|
|
|
|
import { GET_MANGA_READER } from '@/lib/graphql/queries/MangaQuery.ts';
|
|
|
|
|
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx';
|
|
|
|
|
import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder/EmptyViewAbsoluteCentered.tsx';
|
|
|
|
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
|
|
|
|
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
|
|
|
|
|
import { GET_CHAPTERS_READER } from '@/lib/graphql/queries/ChapterQuery.ts';
|
|
|
|
|
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
2024-12-30 16:51:39 +01:00
|
|
|
import { DirectionOffset, TranslationKey } from '@/Base.types.ts';
|
2024-12-03 20:24:55 +01:00
|
|
|
import { TapZoneLayout } from '@/modules/reader/components/TapZoneLayout.tsx';
|
|
|
|
|
import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx';
|
|
|
|
|
import { ReaderRGBAFilter } from '@/modules/reader/components/ReaderRGBAFilter.tsx';
|
|
|
|
|
import { useReaderStateSettingsContext } from '@/modules/reader/contexts/state/ReaderStateSettingsContext.tsx';
|
|
|
|
|
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
|
|
|
|
|
import { ReaderViewer } from '@/modules/reader/components/viewer/ReaderViewer.tsx';
|
|
|
|
|
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
2024-12-30 16:51:39 +01:00
|
|
|
import {
|
|
|
|
|
READER_BACKGROUND_TO_COLOR,
|
|
|
|
|
READING_MODE_VALUE_TO_DISPLAY_DATA,
|
|
|
|
|
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
2024-12-03 20:24:55 +01:00
|
|
|
import { createPageData, createPagesData } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
|
|
|
|
import { ReaderHotkeys } from '@/modules/reader/components/ReaderHotkeys.tsx';
|
2024-12-21 03:46:55 +01:00
|
|
|
import {
|
|
|
|
|
IReaderSettings,
|
2024-12-30 16:51:39 +01:00
|
|
|
IReaderSettingsWithDefaultFlag,
|
2024-12-21 03:46:55 +01:00
|
|
|
ReaderResumeMode,
|
|
|
|
|
ReaderStateChapters,
|
|
|
|
|
ReaderTransitionPageMode,
|
2024-12-26 17:15:50 +01:00
|
|
|
ReadingMode,
|
2024-12-27 00:30:19 +01:00
|
|
|
TReaderAutoScrollContext,
|
2024-12-21 03:46:55 +01:00
|
|
|
TReaderStateMangaContext,
|
|
|
|
|
TReaderStateSettingsContext,
|
|
|
|
|
} from '@/modules/reader/types/Reader.types.ts';
|
2024-12-03 20:24:55 +01:00
|
|
|
import { getInitialReaderPageIndex } from '@/modules/reader/utils/Reader.utils.ts';
|
2024-12-21 23:27:03 +01:00
|
|
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
2024-12-21 03:46:55 +01:00
|
|
|
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
|
|
|
|
|
import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.types.ts';
|
|
|
|
|
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
|
|
|
|
|
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
|
|
|
|
|
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
|
2024-12-26 17:15:50 +01:00
|
|
|
import { isAutoWebtoonMode } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
2024-12-27 00:30:19 +01:00
|
|
|
import { useReaderAutoScrollContext } from '@/modules/reader/contexts/ReaderAutoScrollContext.tsx';
|
2024-12-30 16:51:39 +01:00
|
|
|
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
|
|
|
|
import { TReaderTapZoneContext } from '@/modules/reader/types/TapZoneLayout.types.ts';
|
|
|
|
|
import { useReaderTapZoneContext } from '@/modules/reader/contexts/ReaderTapZoneContext.tsx';
|
2024-12-03 20:24:55 +01:00
|
|
|
|
2024-12-21 03:46:55 +01:00
|
|
|
const BaseReader = ({
|
|
|
|
|
setTitle,
|
|
|
|
|
setOverride,
|
|
|
|
|
readerNavBarWidth,
|
|
|
|
|
isVisible: isOverlayVisible,
|
|
|
|
|
setIsVisible: setIsOverlayVisible,
|
|
|
|
|
manga,
|
|
|
|
|
setManga,
|
|
|
|
|
shouldSkipDupChapters,
|
|
|
|
|
backgroundColor,
|
2024-12-30 16:51:39 +01:00
|
|
|
readingMode,
|
|
|
|
|
tapZoneLayout,
|
|
|
|
|
tapZoneInvertMode,
|
|
|
|
|
shouldShowReadingModePreview,
|
|
|
|
|
shouldShowTapZoneLayoutPreview,
|
2024-12-21 03:46:55 +01:00
|
|
|
setSettings,
|
|
|
|
|
initialChapter,
|
|
|
|
|
currentChapter,
|
|
|
|
|
chapters,
|
|
|
|
|
setReaderStateChapters,
|
|
|
|
|
firstPageUrl,
|
|
|
|
|
totalPages,
|
|
|
|
|
setTotalPages,
|
|
|
|
|
setCurrentPageIndex,
|
|
|
|
|
setPageToScrollToIndex,
|
|
|
|
|
setPages,
|
|
|
|
|
setPageUrls,
|
|
|
|
|
setPageLoadStates,
|
|
|
|
|
setTransitionPageMode,
|
2024-12-27 00:30:19 +01:00
|
|
|
cancelAutoScroll,
|
2024-12-30 16:51:39 +01:00
|
|
|
setShowPreview,
|
2024-12-21 03:46:55 +01:00
|
|
|
}: Pick<NavbarContextType, 'setTitle' | 'setOverride' | 'readerNavBarWidth'> &
|
|
|
|
|
Pick<TReaderOverlayContext, 'isVisible' | 'setIsVisible'> &
|
|
|
|
|
Pick<TReaderStateMangaContext, 'manga' | 'setManga'> &
|
|
|
|
|
Pick<TReaderStateSettingsContext, 'setSettings'> &
|
2024-12-30 16:51:39 +01:00
|
|
|
Pick<
|
|
|
|
|
IReaderSettings,
|
|
|
|
|
'shouldSkipDupChapters' | 'backgroundColor' | 'shouldShowReadingModePreview' | 'shouldShowTapZoneLayoutPreview'
|
|
|
|
|
> &
|
|
|
|
|
Pick<IReaderSettingsWithDefaultFlag, 'readingMode' | 'tapZoneLayout' | 'tapZoneInvertMode'> &
|
2024-12-21 03:46:55 +01:00
|
|
|
Pick<ReaderStateChapters, 'initialChapter' | 'currentChapter' | 'chapters' | 'setReaderStateChapters'> &
|
|
|
|
|
Pick<
|
|
|
|
|
ReaderStatePages,
|
|
|
|
|
| 'totalPages'
|
|
|
|
|
| 'setTotalPages'
|
|
|
|
|
| 'setCurrentPageIndex'
|
|
|
|
|
| 'setPageToScrollToIndex'
|
|
|
|
|
| 'setPages'
|
|
|
|
|
| 'setPageUrls'
|
|
|
|
|
| 'setPageLoadStates'
|
|
|
|
|
| 'setTransitionPageMode'
|
2024-12-30 16:51:39 +01:00
|
|
|
> &
|
|
|
|
|
Pick<TReaderTapZoneContext, 'setShowPreview'> & {
|
2024-12-21 03:46:55 +01:00
|
|
|
firstPageUrl?: string;
|
2024-12-27 00:30:19 +01:00
|
|
|
cancelAutoScroll: TReaderAutoScrollContext['cancel'];
|
2024-12-21 03:46:55 +01:00
|
|
|
}) => {
|
2024-12-03 20:24:55 +01:00
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const { resumeMode } = useLocation<{
|
|
|
|
|
resumeMode: ReaderResumeMode;
|
|
|
|
|
}>().state ?? { resumeMode: ReaderResumeMode.START };
|
|
|
|
|
|
|
|
|
|
const scrollElementRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
|
|
|
|
|
|
const { chapterIndex: paramChapterIndex, mangaId: paramMangaId } = useParams<{
|
|
|
|
|
chapterIndex: string;
|
|
|
|
|
mangaId: string;
|
|
|
|
|
}>();
|
|
|
|
|
const chapterIndex = Number(paramChapterIndex);
|
|
|
|
|
const mangaId = Number(paramMangaId);
|
|
|
|
|
|
|
|
|
|
const mangaResponse = requestManager.useGetManga<GetMangaReaderQuery>(GET_MANGA_READER, mangaId);
|
|
|
|
|
const chaptersResponse = requestManager.useGetMangaChapters<GetChaptersReaderQuery>(GET_CHAPTERS_READER, mangaId);
|
|
|
|
|
const [arePagesFetched, setArePagesFetched] = useState(false);
|
|
|
|
|
const [fetchPages, pagesResponse] = requestManager.useGetChapterPagesFetch(-1);
|
|
|
|
|
|
|
|
|
|
const doFetchPages = useCallback(() => {
|
|
|
|
|
if (!currentChapter) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setArePagesFetched(false);
|
|
|
|
|
|
|
|
|
|
fetchPages({ variables: { input: { chapterId: currentChapter.id } } }).catch(
|
|
|
|
|
defaultPromiseErrorHandler('Reader::fetchPages'),
|
|
|
|
|
);
|
|
|
|
|
}, [fetchPages, currentChapter?.id]);
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
metadata: defaultSettingsMetadata,
|
|
|
|
|
settings: defaultSettings,
|
|
|
|
|
request: defaultSettingsResponse,
|
|
|
|
|
} = useDefaultReaderSettings();
|
|
|
|
|
|
|
|
|
|
const doesChapterExist =
|
|
|
|
|
!chaptersResponse.loading &&
|
|
|
|
|
!chaptersResponse.error &&
|
|
|
|
|
chapterIndex >= 0 &&
|
|
|
|
|
chapterIndex <= chapters.length - 1;
|
|
|
|
|
|
|
|
|
|
const isLoading =
|
|
|
|
|
mangaResponse.loading ||
|
|
|
|
|
chaptersResponse.loading ||
|
|
|
|
|
defaultSettingsResponse.loading ||
|
|
|
|
|
pagesResponse.loading ||
|
|
|
|
|
(doesChapterExist && !arePagesFetched && !chaptersResponse.error && !pagesResponse.error);
|
|
|
|
|
const error = mangaResponse.error ?? chaptersResponse.error ?? defaultSettingsResponse.error ?? pagesResponse.error;
|
|
|
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
|
if (!manga || !currentChapter) {
|
|
|
|
|
setTitle(t('reader.title', { mangaId, chapterIndex }));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setTitle(`${manga.title}: ${currentChapter.name}`);
|
|
|
|
|
}, [t, mangaId, chapterIndex, manga, currentChapter]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
doFetchPages();
|
|
|
|
|
return () => setArePagesFetched(false);
|
|
|
|
|
}, [currentChapter?.id]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setManga(mangaResponse.data?.manga);
|
|
|
|
|
}, [mangaResponse.data?.manga]);
|
|
|
|
|
|
2024-12-09 04:38:33 +01:00
|
|
|
// reset states
|
|
|
|
|
useEffect(
|
|
|
|
|
() => () => {
|
|
|
|
|
setManga(undefined);
|
|
|
|
|
setReaderStateChapters({
|
|
|
|
|
mangaChapters: [],
|
|
|
|
|
chapters: [],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setCurrentPageIndex(0);
|
2024-12-28 01:28:12 +01:00
|
|
|
setPageToScrollToIndex(null);
|
2024-12-09 04:38:33 +01:00
|
|
|
setTotalPages(0);
|
|
|
|
|
setPages([createPageData('', 0)]);
|
|
|
|
|
setPageUrls([]);
|
|
|
|
|
setPageLoadStates([{ loaded: false }]);
|
2024-12-19 01:06:21 +01:00
|
|
|
|
|
|
|
|
setIsOverlayVisible(false);
|
2024-12-27 00:30:19 +01:00
|
|
|
|
|
|
|
|
cancelAutoScroll();
|
2024-12-09 04:38:33 +01:00
|
|
|
},
|
|
|
|
|
[],
|
|
|
|
|
);
|
|
|
|
|
|
2024-12-03 20:24:55 +01:00
|
|
|
// set pages state
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const pagesPayload = pagesResponse.data?.fetchChapterPages;
|
|
|
|
|
if (pagesPayload) {
|
2024-12-09 22:42:51 +01:00
|
|
|
const { pages: newPages } = pagesPayload;
|
2024-12-03 20:24:55 +01:00
|
|
|
|
|
|
|
|
const initialReaderPageIndex = getInitialReaderPageIndex(
|
|
|
|
|
resumeMode,
|
|
|
|
|
currentChapter?.lastPageRead ?? 0,
|
2024-12-09 22:42:51 +01:00
|
|
|
newPages.length - 1,
|
2024-12-03 20:24:55 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
setArePagesFetched(true);
|
|
|
|
|
setTotalPages(pagesPayload.chapter.pageCount);
|
2024-12-09 22:42:51 +01:00
|
|
|
setPages(createPagesData(newPages));
|
|
|
|
|
setPageUrls(newPages);
|
|
|
|
|
setPageLoadStates(newPages.map(() => ({ loaded: false })));
|
2024-12-03 20:24:55 +01:00
|
|
|
setCurrentPageIndex(initialReaderPageIndex);
|
|
|
|
|
setPageToScrollToIndex(initialReaderPageIndex);
|
|
|
|
|
} else {
|
2024-12-09 04:38:33 +01:00
|
|
|
setArePagesFetched(false);
|
2024-12-03 20:24:55 +01:00
|
|
|
setCurrentPageIndex(0);
|
2024-12-28 01:28:12 +01:00
|
|
|
setPageToScrollToIndex(null);
|
2024-12-03 20:24:55 +01:00
|
|
|
setTotalPages(0);
|
|
|
|
|
setPages([createPageData('', 0)]);
|
|
|
|
|
setPageUrls([]);
|
2024-12-07 04:47:28 +01:00
|
|
|
setPageLoadStates([{ loaded: false }]);
|
2024-12-03 20:24:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setTransitionPageMode(ReaderTransitionPageMode.NONE);
|
|
|
|
|
}, [pagesResponse.data?.fetchChapterPages?.pages]);
|
|
|
|
|
|
|
|
|
|
// set settings state
|
|
|
|
|
useEffect(() => {
|
2024-12-26 17:15:50 +01:00
|
|
|
const mangaFromResponse = mangaResponse.data?.manga;
|
|
|
|
|
if (!mangaFromResponse || defaultSettingsResponse.loading || defaultSettingsResponse.error) {
|
2024-12-03 20:24:55 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-26 17:15:50 +01:00
|
|
|
const settingsWithDefaultProfileFallback = getReaderSettingsFor(mangaFromResponse, defaultSettings);
|
|
|
|
|
|
|
|
|
|
const shouldUseWebtoonMode = isAutoWebtoonMode(
|
|
|
|
|
mangaFromResponse,
|
|
|
|
|
settingsWithDefaultProfileFallback.shouldUseAutoWebtoonMode,
|
|
|
|
|
settingsWithDefaultProfileFallback.readingMode,
|
2024-12-03 20:24:55 +01:00
|
|
|
);
|
|
|
|
|
|
2024-12-26 17:15:50 +01:00
|
|
|
const defaultSettingsWithAutoReadingMode = {
|
|
|
|
|
...defaultSettings,
|
|
|
|
|
readingMode: shouldUseWebtoonMode ? ReadingMode.WEBTOON : defaultSettings.readingMode,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const profile = shouldUseWebtoonMode
|
|
|
|
|
? ReadingMode.WEBTOON
|
|
|
|
|
: settingsWithDefaultProfileFallback.readingMode.value;
|
2024-12-03 20:24:55 +01:00
|
|
|
const profileSettings = getReaderSettings(
|
|
|
|
|
'global',
|
|
|
|
|
{ meta: defaultSettingsMetadata! },
|
2024-12-26 17:15:50 +01:00
|
|
|
defaultSettingsWithAutoReadingMode,
|
2024-12-03 20:24:55 +01:00
|
|
|
undefined,
|
|
|
|
|
profile,
|
|
|
|
|
);
|
|
|
|
|
|
2024-12-30 16:51:39 +01:00
|
|
|
const finalSettings = getReaderSettingsFor(mangaFromResponse, profileSettings);
|
|
|
|
|
setSettings(finalSettings);
|
2024-12-03 20:24:55 +01:00
|
|
|
}, [mangaResponse.data?.manga, defaultSettings]);
|
|
|
|
|
|
2024-12-30 16:51:39 +01:00
|
|
|
// show setting previews on change or when open reader
|
|
|
|
|
const previousReadingMode = useRef<IReaderSettingsWithDefaultFlag['readingMode']>();
|
|
|
|
|
const previousTapZoneLayout = useRef<IReaderSettingsWithDefaultFlag['tapZoneLayout']>();
|
|
|
|
|
const previousTapZoneInvertMode = useRef<IReaderSettingsWithDefaultFlag['tapZoneInvertMode']>();
|
2024-12-30 19:41:49 +01:00
|
|
|
const isInitialPreview = useRef(true);
|
2024-12-30 16:51:39 +01:00
|
|
|
useEffect(() => {
|
|
|
|
|
const mangaFromResponse = mangaResponse.data?.manga;
|
|
|
|
|
if (!mangaFromResponse || defaultSettingsResponse.loading || defaultSettingsResponse.error) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const didReadingModeChange = JSON.stringify(readingMode) !== JSON.stringify(previousReadingMode.current);
|
|
|
|
|
const showReadingModePreview = shouldShowReadingModePreview && didReadingModeChange;
|
|
|
|
|
if (showReadingModePreview) {
|
|
|
|
|
makeToast(t(READING_MODE_VALUE_TO_DISPLAY_DATA[readingMode.value].title as TranslationKey));
|
|
|
|
|
}
|
2024-12-30 19:33:37 +01:00
|
|
|
previousReadingMode.current = readingMode;
|
2024-12-30 16:51:39 +01:00
|
|
|
|
|
|
|
|
const didTapZoneLayoutChange =
|
2024-12-30 19:33:37 +01:00
|
|
|
JSON.stringify(tapZoneLayout) !== JSON.stringify(previousTapZoneLayout.current) ||
|
|
|
|
|
JSON.stringify(tapZoneInvertMode) !== JSON.stringify(previousTapZoneInvertMode.current);
|
2024-12-30 16:51:39 +01:00
|
|
|
const showTapZoneLayoutPreview = shouldShowTapZoneLayoutPreview && didTapZoneLayoutChange;
|
|
|
|
|
if (showTapZoneLayoutPreview) {
|
|
|
|
|
setShowPreview(true);
|
2024-12-30 19:41:49 +01:00
|
|
|
if (isInitialPreview.current) {
|
|
|
|
|
setTimeout(() => setShowPreview(false), 5000);
|
|
|
|
|
}
|
2024-12-30 16:51:39 +01:00
|
|
|
}
|
2024-12-30 19:33:37 +01:00
|
|
|
previousTapZoneLayout.current = tapZoneLayout;
|
|
|
|
|
previousTapZoneInvertMode.current = tapZoneInvertMode;
|
2024-12-30 19:41:49 +01:00
|
|
|
|
|
|
|
|
isInitialPreview.current = false;
|
2024-12-30 19:33:37 +01:00
|
|
|
}, [
|
|
|
|
|
readingMode.value,
|
|
|
|
|
readingMode.isDefault,
|
|
|
|
|
tapZoneLayout.value,
|
|
|
|
|
tapZoneLayout.isDefault,
|
|
|
|
|
tapZoneInvertMode.value,
|
|
|
|
|
tapZoneInvertMode.isDefault,
|
|
|
|
|
]);
|
2024-12-30 16:51:39 +01:00
|
|
|
|
2024-12-03 20:24:55 +01:00
|
|
|
// set chapters state
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const newMangaChapters = chaptersResponse.data?.chapters.nodes;
|
|
|
|
|
const newCurrentChapter = newMangaChapters
|
|
|
|
|
? (newMangaChapters[newMangaChapters.length - chapterIndex] ?? null)
|
|
|
|
|
: undefined;
|
|
|
|
|
const newInitialChapter = initialChapter ?? newCurrentChapter;
|
|
|
|
|
|
|
|
|
|
setReaderStateChapters({
|
|
|
|
|
mangaChapters: newMangaChapters ?? [],
|
|
|
|
|
chapters:
|
|
|
|
|
newInitialChapter && newMangaChapters
|
|
|
|
|
? Chapters.removeDuplicates(newInitialChapter, newMangaChapters)
|
|
|
|
|
: [],
|
|
|
|
|
initialChapter: newInitialChapter,
|
|
|
|
|
currentChapter: newCurrentChapter,
|
|
|
|
|
nextChapter:
|
|
|
|
|
newMangaChapters &&
|
|
|
|
|
newCurrentChapter &&
|
|
|
|
|
Chapters.getNextChapter(newCurrentChapter, newMangaChapters, {
|
|
|
|
|
offset: DirectionOffset.NEXT,
|
|
|
|
|
skipDupe: shouldSkipDupChapters,
|
|
|
|
|
skipDupeChapter: newInitialChapter,
|
|
|
|
|
}),
|
|
|
|
|
previousChapter:
|
|
|
|
|
newMangaChapters &&
|
|
|
|
|
newCurrentChapter &&
|
|
|
|
|
Chapters.getNextChapter(newCurrentChapter, newMangaChapters, {
|
|
|
|
|
offset: DirectionOffset.PREVIOUS,
|
|
|
|
|
skipDupe: shouldSkipDupChapters,
|
|
|
|
|
skipDupeChapter: newInitialChapter,
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
}, [chaptersResponse.data?.chapters.nodes, chapterIndex, shouldSkipDupChapters]);
|
|
|
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
|
setOverride({
|
|
|
|
|
status: true,
|
|
|
|
|
value: (
|
|
|
|
|
<Box sx={{ position: 'absolute' }}>
|
2024-12-24 17:21:25 +01:00
|
|
|
<ReaderHotkeys scrollElementRef={scrollElementRef} />
|
2024-12-03 20:24:55 +01:00
|
|
|
<ReaderOverlay isVisible={isOverlayVisible} />
|
|
|
|
|
{!scrollElementRef.current && (
|
|
|
|
|
<Box
|
|
|
|
|
onClick={() => setIsOverlayVisible(!isOverlayVisible)}
|
|
|
|
|
sx={{
|
2024-12-24 17:25:12 +01:00
|
|
|
position: 'fixed',
|
|
|
|
|
top: 0,
|
|
|
|
|
left: 0,
|
2024-12-03 20:24:55 +01:00
|
|
|
width: '100vw',
|
|
|
|
|
height: '100vh',
|
|
|
|
|
background: 'transparent',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</Box>
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return () => setOverride({ status: false, value: null });
|
|
|
|
|
}, [isOverlayVisible, setIsOverlayVisible, scrollElementRef.current]);
|
|
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
return (
|
|
|
|
|
<Box
|
|
|
|
|
sx={{
|
|
|
|
|
height: '100vh',
|
|
|
|
|
width: '100vw',
|
|
|
|
|
display: 'grid',
|
|
|
|
|
placeItems: 'center',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<LoadingPlaceholder />
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
return (
|
|
|
|
|
<EmptyViewAbsoluteCentered
|
|
|
|
|
message={t('global.error.label.failed_to_load_data')}
|
2024-12-21 23:27:03 +01:00
|
|
|
messageExtra={getErrorMessage(error)}
|
2024-12-03 20:24:55 +01:00
|
|
|
retry={() => {
|
|
|
|
|
if (mangaResponse.error) {
|
|
|
|
|
mangaResponse.refetch().catch(defaultPromiseErrorHandler('Reader::refetchManga'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (defaultSettingsResponse.error) {
|
|
|
|
|
defaultSettingsResponse
|
|
|
|
|
.refetch()
|
|
|
|
|
.catch(defaultPromiseErrorHandler('Reader::refetchDefaultSettings'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chaptersResponse.error) {
|
|
|
|
|
chaptersResponse.refetch().catch(defaultPromiseErrorHandler('Reader::refetchChapters'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pagesResponse.error) {
|
|
|
|
|
doFetchPages();
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentChapter === null) {
|
|
|
|
|
return <EmptyViewAbsoluteCentered message={t('reader.error.label.chapter_not_found')} />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentChapter && !currentChapter?.pageCount) {
|
|
|
|
|
return <EmptyViewAbsoluteCentered message={t('reader.error.label.no_pages_found')} retry={doFetchPages} />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!manga || !currentChapter) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-21 03:46:55 +01:00
|
|
|
const isPlaceholderPageState = totalPages === 1 && firstPageUrl === requestManager.getBaseUrl();
|
2024-12-09 04:40:24 +01:00
|
|
|
if (isPlaceholderPageState) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 20:24:55 +01:00
|
|
|
return (
|
2024-12-24 17:21:25 +01:00
|
|
|
<Box
|
|
|
|
|
sx={{
|
|
|
|
|
position: 'relative',
|
|
|
|
|
display: 'flex',
|
|
|
|
|
flexDirection: 'column',
|
|
|
|
|
width: `calc(100vw - ${readerNavBarWidth}px)`,
|
|
|
|
|
height: `100vh`,
|
|
|
|
|
marginLeft: `${readerNavBarWidth}px`,
|
|
|
|
|
transition: (theme) =>
|
|
|
|
|
`width 0.${theme.transitions.duration.shortest}s, margin-left 0.${theme.transitions.duration.shortest}s`,
|
|
|
|
|
overflow: 'auto',
|
|
|
|
|
backgroundColor: READER_BACKGROUND_TO_COLOR[backgroundColor],
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ReaderViewer ref={scrollElementRef} />
|
|
|
|
|
<TapZoneLayout />
|
|
|
|
|
<ReaderRGBAFilter />
|
|
|
|
|
</Box>
|
2024-12-03 20:24:55 +01:00
|
|
|
);
|
|
|
|
|
};
|
2024-12-21 03:46:55 +01:00
|
|
|
|
|
|
|
|
export const Reader = withPropsFrom(
|
|
|
|
|
memo(BaseReader),
|
|
|
|
|
[
|
|
|
|
|
useNavBarContext,
|
|
|
|
|
useReaderOverlayContext,
|
|
|
|
|
useReaderStateMangaContext,
|
|
|
|
|
useReaderStateChaptersContext,
|
|
|
|
|
useReaderStateSettingsContext,
|
2024-12-30 16:51:39 +01:00
|
|
|
() => {
|
|
|
|
|
const {
|
|
|
|
|
shouldSkipDupChapters,
|
|
|
|
|
backgroundColor,
|
|
|
|
|
shouldShowReadingModePreview,
|
|
|
|
|
shouldShowTapZoneLayoutPreview,
|
|
|
|
|
} = ReaderService.useSettingsWithoutDefaultFlag();
|
|
|
|
|
return {
|
|
|
|
|
shouldSkipDupChapters,
|
|
|
|
|
backgroundColor,
|
|
|
|
|
shouldShowReadingModePreview,
|
|
|
|
|
shouldShowTapZoneLayoutPreview,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
const { readingMode, tapZoneLayout, tapZoneInvertMode } = ReaderService.useSettings();
|
|
|
|
|
return { readingMode, tapZoneLayout, tapZoneInvertMode };
|
|
|
|
|
},
|
2024-12-21 03:46:55 +01:00
|
|
|
userReaderStatePagesContext,
|
|
|
|
|
() => ({
|
|
|
|
|
firstPageUrl: userReaderStatePagesContext().pages[0].primary.url,
|
|
|
|
|
}),
|
2024-12-27 00:30:19 +01:00
|
|
|
() => ({ cancelAutoScroll: useReaderAutoScrollContext().cancel }),
|
2024-12-30 16:51:39 +01:00
|
|
|
useReaderTapZoneContext,
|
2024-12-21 03:46:55 +01:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'setTitle',
|
|
|
|
|
'setOverride',
|
|
|
|
|
'readerNavBarWidth',
|
|
|
|
|
'isVisible',
|
|
|
|
|
'setIsVisible',
|
|
|
|
|
'manga',
|
|
|
|
|
'setManga',
|
|
|
|
|
'shouldSkipDupChapters',
|
2024-12-22 22:03:29 +01:00
|
|
|
'backgroundColor',
|
2024-12-30 16:51:39 +01:00
|
|
|
'readingMode',
|
|
|
|
|
'tapZoneLayout',
|
|
|
|
|
'tapZoneInvertMode',
|
|
|
|
|
'shouldShowReadingModePreview',
|
|
|
|
|
'shouldShowTapZoneLayoutPreview',
|
2024-12-21 03:46:55 +01:00
|
|
|
'setSettings',
|
|
|
|
|
'initialChapter',
|
|
|
|
|
'currentChapter',
|
|
|
|
|
'chapters',
|
|
|
|
|
'setReaderStateChapters',
|
|
|
|
|
'firstPageUrl',
|
|
|
|
|
'totalPages',
|
|
|
|
|
'setTotalPages',
|
|
|
|
|
'setCurrentPageIndex',
|
|
|
|
|
'setPageToScrollToIndex',
|
|
|
|
|
'setPages',
|
|
|
|
|
'setPageUrls',
|
|
|
|
|
'setPageLoadStates',
|
|
|
|
|
'setTransitionPageMode',
|
2024-12-27 00:30:19 +01:00
|
|
|
'cancelAutoScroll',
|
2024-12-30 16:51:39 +01:00
|
|
|
'setShowPreview',
|
2024-12-21 03:46:55 +01:00
|
|
|
],
|
|
|
|
|
);
|