Extract logic from "Reader" into hooks
This commit is contained in:
60
src/modules/reader/hooks/useReaderResetStates.ts
Normal file
60
src/modules/reader/hooks/useReaderResetStates.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useEffect } from 'react';
|
||||||
|
import { createPageData } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
||||||
|
import {
|
||||||
|
ReaderStateChapters,
|
||||||
|
ReaderTransitionPageMode,
|
||||||
|
TReaderAutoScrollContext,
|
||||||
|
TReaderStateMangaContext,
|
||||||
|
TReaderStateSettingsContext,
|
||||||
|
} from '@/modules/reader/types/Reader.types.ts';
|
||||||
|
import { DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG } from '@/modules/reader/services/ReaderSettingsMetadata.ts';
|
||||||
|
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
|
||||||
|
import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.types.ts';
|
||||||
|
|
||||||
|
export const useReaderResetStates = (
|
||||||
|
setManga: TReaderStateMangaContext['setManga'],
|
||||||
|
setReaderStateChapters: ReaderStateChapters['setReaderStateChapters'],
|
||||||
|
setCurrentPageIndex: ReaderStatePages['setCurrentPageIndex'],
|
||||||
|
setPageToScrollToIndex: ReaderStatePages['setPageToScrollToIndex'],
|
||||||
|
setTotalPages: ReaderStatePages['setTotalPages'],
|
||||||
|
setPages: ReaderStatePages['setPages'],
|
||||||
|
setPageUrls: ReaderStatePages['setPageUrls'],
|
||||||
|
setPageLoadStates: ReaderStatePages['setPageLoadStates'],
|
||||||
|
setTransitionPageMode: ReaderStatePages['setTransitionPageMode'],
|
||||||
|
setIsOverlayVisible: TReaderOverlayContext['setIsVisible'],
|
||||||
|
setSettings: TReaderStateSettingsContext['setSettings'],
|
||||||
|
cancelAutoScroll: TReaderAutoScrollContext['cancel'],
|
||||||
|
) => {
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
setManga(undefined);
|
||||||
|
setReaderStateChapters({
|
||||||
|
mangaChapters: [],
|
||||||
|
chapters: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
setCurrentPageIndex(0);
|
||||||
|
setPageToScrollToIndex(null);
|
||||||
|
setTotalPages(0);
|
||||||
|
setPages([createPageData('', 0)]);
|
||||||
|
setPageUrls([]);
|
||||||
|
setPageLoadStates([{ url: '', loaded: false }]);
|
||||||
|
setTransitionPageMode(ReaderTransitionPageMode.NONE);
|
||||||
|
|
||||||
|
setIsOverlayVisible(false);
|
||||||
|
|
||||||
|
setSettings(DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG);
|
||||||
|
|
||||||
|
cancelAutoScroll();
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
};
|
||||||
56
src/modules/reader/hooks/useReaderSetChaptersState.ts
Normal file
56
src/modules/reader/hooks/useReaderSetChaptersState.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useEffect } from 'react';
|
||||||
|
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||||
|
import { DirectionOffset } from '@/Base.types.ts';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
import { GetChaptersReaderQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||||
|
import { IReaderSettings, ReaderStateChapters } from '@/modules/reader/types/Reader.types.ts';
|
||||||
|
|
||||||
|
export const useReaderSetChaptersState = (
|
||||||
|
chaptersResponse: ReturnType<typeof requestManager.useGetMangaChapters<GetChaptersReaderQuery>>,
|
||||||
|
chapterSourceOrder: number,
|
||||||
|
initialChapter: ReaderStateChapters['initialChapter'],
|
||||||
|
setReaderStateChapters: ReaderStateChapters['setReaderStateChapters'],
|
||||||
|
shouldSkipDupChapters: IReaderSettings['shouldSkipDupChapters'],
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const newMangaChapters = chaptersResponse.data?.chapters.nodes;
|
||||||
|
const newCurrentChapter = newMangaChapters
|
||||||
|
? (newMangaChapters[newMangaChapters.length - chapterSourceOrder] ?? 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, chapterSourceOrder, shouldSkipDupChapters]);
|
||||||
|
};
|
||||||
65
src/modules/reader/hooks/useReaderSetPagesState.ts
Normal file
65
src/modules/reader/hooks/useReaderSetPagesState.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useEffect } from 'react';
|
||||||
|
import { getInitialReaderPageIndex } from '@/modules/reader/utils/Reader.utils.ts';
|
||||||
|
import { createPageData, createPagesData } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
||||||
|
import {
|
||||||
|
ReaderResumeMode,
|
||||||
|
ReaderStateChapters,
|
||||||
|
ReaderTransitionPageMode,
|
||||||
|
} from '@/modules/reader/types/Reader.types.ts';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
|
||||||
|
|
||||||
|
export const useReaderSetPagesState = (
|
||||||
|
pagesResponse: ReturnType<typeof requestManager.useGetChapterPagesFetch>[1],
|
||||||
|
resumeMode: ReaderResumeMode,
|
||||||
|
currentChapter: ReaderStateChapters['currentChapter'],
|
||||||
|
setArePagesFetched: (fetched: boolean) => void,
|
||||||
|
setTotalPages: ReaderStatePages['setTotalPages'],
|
||||||
|
setPages: ReaderStatePages['setPages'],
|
||||||
|
setPageUrls: ReaderStatePages['setPageUrls'],
|
||||||
|
setPageLoadStates: ReaderStatePages['setPageLoadStates'],
|
||||||
|
setCurrentPageIndex: ReaderStatePages['setCurrentPageIndex'],
|
||||||
|
setPageToScrollToIndex: ReaderStatePages['setPageToScrollToIndex'],
|
||||||
|
setTransitionPageMode: ReaderStatePages['setTransitionPageMode'],
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const pagesPayload = pagesResponse.data?.fetchChapterPages;
|
||||||
|
if (pagesPayload) {
|
||||||
|
const { pages: newPages } = pagesPayload;
|
||||||
|
|
||||||
|
const initialReaderPageIndex = getInitialReaderPageIndex(
|
||||||
|
resumeMode,
|
||||||
|
currentChapter?.lastPageRead ?? 0,
|
||||||
|
newPages.length - 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const newPageData = createPagesData(newPages);
|
||||||
|
|
||||||
|
setArePagesFetched(true);
|
||||||
|
setTotalPages(pagesPayload.chapter.pageCount);
|
||||||
|
setPages(newPageData);
|
||||||
|
setPageUrls(newPages);
|
||||||
|
setPageLoadStates(newPageData.map(({ primary: { url } }) => ({ url, loaded: false })));
|
||||||
|
setCurrentPageIndex(initialReaderPageIndex);
|
||||||
|
setPageToScrollToIndex(initialReaderPageIndex);
|
||||||
|
} else {
|
||||||
|
setArePagesFetched(false);
|
||||||
|
setCurrentPageIndex(0);
|
||||||
|
setPageToScrollToIndex(null);
|
||||||
|
setTotalPages(0);
|
||||||
|
setPages([createPageData('', 0)]);
|
||||||
|
setPageUrls([]);
|
||||||
|
setPageLoadStates([{ url: '', loaded: false }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTransitionPageMode(ReaderTransitionPageMode.NONE);
|
||||||
|
}, [pagesResponse.data?.fetchChapterPages?.pages]);
|
||||||
|
};
|
||||||
62
src/modules/reader/hooks/useReaderSetSettingsState.ts
Normal file
62
src/modules/reader/hooks/useReaderSetSettingsState.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
getReaderSettings,
|
||||||
|
getReaderSettingsFor,
|
||||||
|
useDefaultReaderSettings,
|
||||||
|
} from '@/modules/reader/services/ReaderSettingsMetadata.ts';
|
||||||
|
import { isAutoWebtoonMode } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||||
|
import { ReadingMode, TReaderStateSettingsContext } from '@/modules/reader/types/Reader.types.ts';
|
||||||
|
import { GetMangaReaderQuery } from '@/lib/graphql/generated/graphql.ts';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
|
||||||
|
export const useReaderSetSettingsState = (
|
||||||
|
mangaResponse: ReturnType<typeof requestManager.useGetManga<GetMangaReaderQuery>>,
|
||||||
|
defaultSettingsResponse: ReturnType<typeof useDefaultReaderSettings>['request'],
|
||||||
|
defaultSettings: ReturnType<typeof useDefaultReaderSettings>['settings'],
|
||||||
|
defaultSettingsMetadata: ReturnType<typeof useDefaultReaderSettings>['metadata'],
|
||||||
|
setSettings: TReaderStateSettingsContext['setSettings'],
|
||||||
|
setAreSettingsSet: (areSet: boolean) => void,
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const mangaFromResponse = mangaResponse.data?.manga;
|
||||||
|
if (!mangaFromResponse || defaultSettingsResponse.loading || defaultSettingsResponse.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsWithDefaultProfileFallback = getReaderSettingsFor(mangaFromResponse, defaultSettings);
|
||||||
|
|
||||||
|
const shouldUseWebtoonMode = isAutoWebtoonMode(
|
||||||
|
mangaFromResponse,
|
||||||
|
settingsWithDefaultProfileFallback.shouldUseAutoWebtoonMode,
|
||||||
|
settingsWithDefaultProfileFallback.readingMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultSettingsWithAutoReadingMode = {
|
||||||
|
...defaultSettings,
|
||||||
|
readingMode: shouldUseWebtoonMode ? ReadingMode.WEBTOON : defaultSettings.readingMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
const profile = shouldUseWebtoonMode
|
||||||
|
? ReadingMode.WEBTOON
|
||||||
|
: settingsWithDefaultProfileFallback.readingMode.value;
|
||||||
|
const profileSettings = getReaderSettings(
|
||||||
|
'global',
|
||||||
|
{ meta: defaultSettingsMetadata! },
|
||||||
|
defaultSettingsWithAutoReadingMode,
|
||||||
|
undefined,
|
||||||
|
profile,
|
||||||
|
);
|
||||||
|
|
||||||
|
const finalSettings = getReaderSettingsFor(mangaFromResponse, profileSettings);
|
||||||
|
setSettings(finalSettings);
|
||||||
|
setAreSettingsSet(true);
|
||||||
|
}, [mangaResponse.data?.manga, defaultSettings]);
|
||||||
|
};
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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 { useEffect, useRef } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { IReaderSettings, IReaderSettingsWithDefaultFlag } from '@/modules/reader/types/Reader.types.ts';
|
||||||
|
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
||||||
|
import { READING_MODE_VALUE_TO_DISPLAY_DATA } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||||
|
import { TranslationKey } from '@/Base.types.ts';
|
||||||
|
import { TReaderTapZoneContext } from '@/modules/reader/types/TapZoneLayout.types.ts';
|
||||||
|
|
||||||
|
const HIDE_PREVIEW_TIMEOUT = 5000;
|
||||||
|
|
||||||
|
export const useReaderShowSettingPreviewOnChange = (
|
||||||
|
isLoading: boolean,
|
||||||
|
error: any,
|
||||||
|
areSettingsSet: boolean,
|
||||||
|
readingMode: IReaderSettingsWithDefaultFlag['readingMode'],
|
||||||
|
tapZoneLayout: IReaderSettingsWithDefaultFlag['tapZoneLayout'],
|
||||||
|
tapZoneInvertMode: IReaderSettingsWithDefaultFlag['tapZoneInvertMode'],
|
||||||
|
shouldShowReadingModePreview: IReaderSettings['shouldShowReadingModePreview'],
|
||||||
|
shouldShowTapZoneLayoutPreview: IReaderSettings['shouldShowTapZoneLayoutPreview'],
|
||||||
|
setShowPreview: TReaderTapZoneContext['setShowPreview'],
|
||||||
|
) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// show setting previews on change or when open reader
|
||||||
|
const previousReadingMode = useRef<IReaderSettingsWithDefaultFlag['readingMode']>();
|
||||||
|
const previousTapZoneLayout = useRef<IReaderSettingsWithDefaultFlag['tapZoneLayout']>();
|
||||||
|
const previousTapZoneInvertMode = useRef<IReaderSettingsWithDefaultFlag['tapZoneInvertMode']>();
|
||||||
|
const isInitialPreview = useRef(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isLoading || error || !areSettingsSet) {
|
||||||
|
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), {
|
||||||
|
autoHideDuration: HIDE_PREVIEW_TIMEOUT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
previousReadingMode.current = readingMode;
|
||||||
|
|
||||||
|
const didTapZoneLayoutChange =
|
||||||
|
JSON.stringify(tapZoneLayout) !== JSON.stringify(previousTapZoneLayout.current) ||
|
||||||
|
JSON.stringify(tapZoneInvertMode) !== JSON.stringify(previousTapZoneInvertMode.current);
|
||||||
|
const showTapZoneLayoutPreview = shouldShowTapZoneLayoutPreview && didTapZoneLayoutChange;
|
||||||
|
if (showTapZoneLayoutPreview) {
|
||||||
|
setShowPreview(true);
|
||||||
|
if (isInitialPreview.current) {
|
||||||
|
setTimeout(() => setShowPreview(false), HIDE_PREVIEW_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousTapZoneLayout.current = tapZoneLayout;
|
||||||
|
previousTapZoneInvertMode.current = tapZoneInvertMode;
|
||||||
|
|
||||||
|
isInitialPreview.current = false;
|
||||||
|
}, [
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
areSettingsSet,
|
||||||
|
readingMode.value,
|
||||||
|
readingMode.isDefault,
|
||||||
|
tapZoneLayout.value,
|
||||||
|
tapZoneLayout.isDefault,
|
||||||
|
tapZoneInvertMode.value,
|
||||||
|
tapZoneInvertMode.isDefault,
|
||||||
|
]);
|
||||||
|
};
|
||||||
@@ -10,12 +10,7 @@ import Box from '@mui/material/Box';
|
|||||||
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import { useDefaultReaderSettings } from '@/modules/reader/services/ReaderSettingsMetadata.ts';
|
||||||
DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG,
|
|
||||||
getReaderSettings,
|
|
||||||
getReaderSettingsFor,
|
|
||||||
useDefaultReaderSettings,
|
|
||||||
} from '@/modules/reader/services/ReaderSettingsMetadata.ts';
|
|
||||||
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
||||||
import { ReaderOverlay } from '@/modules/reader/components/overlay/ReaderOverlay.tsx';
|
import { ReaderOverlay } from '@/modules/reader/components/overlay/ReaderOverlay.tsx';
|
||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
@@ -26,8 +21,6 @@ import { EmptyViewAbsoluteCentered } from '@/modules/core/components/placeholder
|
|||||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||||
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
|
import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/ReaderStatePagesContext.tsx';
|
||||||
import { GET_CHAPTERS_READER } from '@/lib/graphql/queries/ChapterQuery.ts';
|
import { GET_CHAPTERS_READER } from '@/lib/graphql/queries/ChapterQuery.ts';
|
||||||
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
|
||||||
import { DirectionOffset, TranslationKey } from '@/Base.types.ts';
|
|
||||||
import { TapZoneLayout } from '@/modules/reader/components/TapZoneLayout.tsx';
|
import { TapZoneLayout } from '@/modules/reader/components/TapZoneLayout.tsx';
|
||||||
import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx';
|
import { useReaderOverlayContext } from '@/modules/reader/contexts/ReaderOverlayContext.tsx';
|
||||||
import { ReaderRGBAFilter } from '@/modules/reader/components/ReaderRGBAFilter.tsx';
|
import { ReaderRGBAFilter } from '@/modules/reader/components/ReaderRGBAFilter.tsx';
|
||||||
@@ -35,35 +28,31 @@ import { useReaderStateSettingsContext } from '@/modules/reader/contexts/state/R
|
|||||||
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
|
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
|
||||||
import { ReaderViewer } from '@/modules/reader/components/viewer/ReaderViewer.tsx';
|
import { ReaderViewer } from '@/modules/reader/components/viewer/ReaderViewer.tsx';
|
||||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||||
import {
|
import { READER_BACKGROUND_TO_COLOR } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||||
READER_BACKGROUND_TO_COLOR,
|
|
||||||
READING_MODE_VALUE_TO_DISPLAY_DATA,
|
|
||||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
|
||||||
import { createPageData, createPagesData } from '@/modules/reader/utils/ReaderPager.utils.tsx';
|
|
||||||
import { ReaderHotkeys } from '@/modules/reader/components/ReaderHotkeys.tsx';
|
import { ReaderHotkeys } from '@/modules/reader/components/ReaderHotkeys.tsx';
|
||||||
import {
|
import {
|
||||||
IReaderSettings,
|
IReaderSettings,
|
||||||
IReaderSettingsWithDefaultFlag,
|
IReaderSettingsWithDefaultFlag,
|
||||||
ReaderResumeMode,
|
ReaderResumeMode,
|
||||||
ReaderStateChapters,
|
ReaderStateChapters,
|
||||||
ReaderTransitionPageMode,
|
|
||||||
ReadingMode,
|
|
||||||
TReaderAutoScrollContext,
|
TReaderAutoScrollContext,
|
||||||
TReaderStateMangaContext,
|
TReaderStateMangaContext,
|
||||||
TReaderStateSettingsContext,
|
TReaderStateSettingsContext,
|
||||||
} from '@/modules/reader/types/Reader.types.ts';
|
} from '@/modules/reader/types/Reader.types.ts';
|
||||||
import { getInitialReaderPageIndex } from '@/modules/reader/utils/Reader.utils.ts';
|
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
|
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
|
||||||
import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.types.ts';
|
import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.types.ts';
|
||||||
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
|
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
|
||||||
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
|
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
|
||||||
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
|
import { useReaderStateChaptersContext } from '@/modules/reader/contexts/state/ReaderStateChaptersContext.tsx';
|
||||||
import { isAutoWebtoonMode } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
|
||||||
import { useReaderAutoScrollContext } from '@/modules/reader/contexts/ReaderAutoScrollContext.tsx';
|
import { useReaderAutoScrollContext } from '@/modules/reader/contexts/ReaderAutoScrollContext.tsx';
|
||||||
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
|
||||||
import { TReaderTapZoneContext } from '@/modules/reader/types/TapZoneLayout.types.ts';
|
import { TReaderTapZoneContext } from '@/modules/reader/types/TapZoneLayout.types.ts';
|
||||||
import { useReaderTapZoneContext } from '@/modules/reader/contexts/ReaderTapZoneContext.tsx';
|
import { useReaderTapZoneContext } from '@/modules/reader/contexts/ReaderTapZoneContext.tsx';
|
||||||
|
import { useReaderResetStates } from '@/modules/reader/hooks/useReaderResetStates.ts';
|
||||||
|
import { useReaderSetPagesState } from '@/modules/reader/hooks/useReaderSetPagesState.ts';
|
||||||
|
import { useReaderSetSettingsState } from '@/modules/reader/hooks/useReaderSetSettingsState.ts';
|
||||||
|
import { useReaderShowSettingPreviewOnChange } from '@/modules/reader/hooks/useReaderShowSettingPreviewOnChange.ts';
|
||||||
|
import { useReaderSetChaptersState } from '@/modules/reader/hooks/useReaderSetChaptersState.ts';
|
||||||
|
|
||||||
const BaseReader = ({
|
const BaseReader = ({
|
||||||
setTitle,
|
setTitle,
|
||||||
@@ -194,183 +183,59 @@ const BaseReader = ({
|
|||||||
setManga(mangaResponse.data?.manga);
|
setManga(mangaResponse.data?.manga);
|
||||||
}, [mangaResponse.data?.manga]);
|
}, [mangaResponse.data?.manga]);
|
||||||
|
|
||||||
// reset states
|
useReaderResetStates(
|
||||||
useEffect(
|
setManga,
|
||||||
() => () => {
|
setReaderStateChapters,
|
||||||
setManga(undefined);
|
setCurrentPageIndex,
|
||||||
setReaderStateChapters({
|
setPageToScrollToIndex,
|
||||||
mangaChapters: [],
|
setTotalPages,
|
||||||
chapters: [],
|
setPages,
|
||||||
});
|
setPageUrls,
|
||||||
|
setPageLoadStates,
|
||||||
setCurrentPageIndex(0);
|
setTransitionPageMode,
|
||||||
setPageToScrollToIndex(null);
|
setIsOverlayVisible,
|
||||||
setTotalPages(0);
|
setSettings,
|
||||||
setPages([createPageData('', 0)]);
|
cancelAutoScroll,
|
||||||
setPageUrls([]);
|
|
||||||
setPageLoadStates([{ url: '', loaded: false }]);
|
|
||||||
setTransitionPageMode(ReaderTransitionPageMode.NONE);
|
|
||||||
|
|
||||||
setIsOverlayVisible(false);
|
|
||||||
|
|
||||||
setSettings(DEFAULT_READER_SETTINGS_WITH_DEFAULT_FLAG);
|
|
||||||
|
|
||||||
cancelAutoScroll();
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
);
|
||||||
|
useReaderSetPagesState(
|
||||||
// set pages state
|
pagesResponse,
|
||||||
useEffect(() => {
|
resumeMode,
|
||||||
const pagesPayload = pagesResponse.data?.fetchChapterPages;
|
currentChapter,
|
||||||
if (pagesPayload) {
|
setArePagesFetched,
|
||||||
const { pages: newPages } = pagesPayload;
|
setTotalPages,
|
||||||
|
setPages,
|
||||||
const initialReaderPageIndex = getInitialReaderPageIndex(
|
setPageUrls,
|
||||||
resumeMode,
|
setPageLoadStates,
|
||||||
currentChapter?.lastPageRead ?? 0,
|
setCurrentPageIndex,
|
||||||
newPages.length - 1,
|
setPageToScrollToIndex,
|
||||||
);
|
setTransitionPageMode,
|
||||||
|
);
|
||||||
const newPageData = createPagesData(newPages);
|
useReaderSetSettingsState(
|
||||||
|
mangaResponse,
|
||||||
setArePagesFetched(true);
|
defaultSettingsResponse,
|
||||||
setTotalPages(pagesPayload.chapter.pageCount);
|
defaultSettings,
|
||||||
setPages(newPageData);
|
defaultSettingsMetadata,
|
||||||
setPageUrls(newPages);
|
setSettings,
|
||||||
setPageLoadStates(newPageData.map(({ primary: { url } }) => ({ url, loaded: false })));
|
setAreSettingsSet,
|
||||||
setCurrentPageIndex(initialReaderPageIndex);
|
);
|
||||||
setPageToScrollToIndex(initialReaderPageIndex);
|
useReaderShowSettingPreviewOnChange(
|
||||||
} else {
|
|
||||||
setArePagesFetched(false);
|
|
||||||
setCurrentPageIndex(0);
|
|
||||||
setPageToScrollToIndex(null);
|
|
||||||
setTotalPages(0);
|
|
||||||
setPages([createPageData('', 0)]);
|
|
||||||
setPageUrls([]);
|
|
||||||
setPageLoadStates([{ url: '', loaded: false }]);
|
|
||||||
}
|
|
||||||
|
|
||||||
setTransitionPageMode(ReaderTransitionPageMode.NONE);
|
|
||||||
}, [pagesResponse.data?.fetchChapterPages?.pages]);
|
|
||||||
|
|
||||||
// set settings state
|
|
||||||
useEffect(() => {
|
|
||||||
const mangaFromResponse = mangaResponse.data?.manga;
|
|
||||||
if (!mangaFromResponse || defaultSettingsResponse.loading || defaultSettingsResponse.error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const settingsWithDefaultProfileFallback = getReaderSettingsFor(mangaFromResponse, defaultSettings);
|
|
||||||
|
|
||||||
const shouldUseWebtoonMode = isAutoWebtoonMode(
|
|
||||||
mangaFromResponse,
|
|
||||||
settingsWithDefaultProfileFallback.shouldUseAutoWebtoonMode,
|
|
||||||
settingsWithDefaultProfileFallback.readingMode,
|
|
||||||
);
|
|
||||||
|
|
||||||
const defaultSettingsWithAutoReadingMode = {
|
|
||||||
...defaultSettings,
|
|
||||||
readingMode: shouldUseWebtoonMode ? ReadingMode.WEBTOON : defaultSettings.readingMode,
|
|
||||||
};
|
|
||||||
|
|
||||||
const profile = shouldUseWebtoonMode
|
|
||||||
? ReadingMode.WEBTOON
|
|
||||||
: settingsWithDefaultProfileFallback.readingMode.value;
|
|
||||||
const profileSettings = getReaderSettings(
|
|
||||||
'global',
|
|
||||||
{ meta: defaultSettingsMetadata! },
|
|
||||||
defaultSettingsWithAutoReadingMode,
|
|
||||||
undefined,
|
|
||||||
profile,
|
|
||||||
);
|
|
||||||
|
|
||||||
const finalSettings = getReaderSettingsFor(mangaFromResponse, profileSettings);
|
|
||||||
setSettings(finalSettings);
|
|
||||||
setAreSettingsSet(true);
|
|
||||||
}, [mangaResponse.data?.manga, defaultSettings]);
|
|
||||||
|
|
||||||
// show setting previews on change or when open reader
|
|
||||||
const previousReadingMode = useRef<IReaderSettingsWithDefaultFlag['readingMode']>();
|
|
||||||
const previousTapZoneLayout = useRef<IReaderSettingsWithDefaultFlag['tapZoneLayout']>();
|
|
||||||
const previousTapZoneInvertMode = useRef<IReaderSettingsWithDefaultFlag['tapZoneInvertMode']>();
|
|
||||||
const isInitialPreview = useRef(true);
|
|
||||||
useEffect(() => {
|
|
||||||
if (isLoading || error || !areSettingsSet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const HIDE_PREVIEW_TIMEOUT = 5000;
|
|
||||||
|
|
||||||
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), {
|
|
||||||
autoHideDuration: HIDE_PREVIEW_TIMEOUT,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
previousReadingMode.current = readingMode;
|
|
||||||
|
|
||||||
const didTapZoneLayoutChange =
|
|
||||||
JSON.stringify(tapZoneLayout) !== JSON.stringify(previousTapZoneLayout.current) ||
|
|
||||||
JSON.stringify(tapZoneInvertMode) !== JSON.stringify(previousTapZoneInvertMode.current);
|
|
||||||
const showTapZoneLayoutPreview = shouldShowTapZoneLayoutPreview && didTapZoneLayoutChange;
|
|
||||||
if (showTapZoneLayoutPreview) {
|
|
||||||
setShowPreview(true);
|
|
||||||
if (isInitialPreview.current) {
|
|
||||||
setTimeout(() => setShowPreview(false), HIDE_PREVIEW_TIMEOUT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previousTapZoneLayout.current = tapZoneLayout;
|
|
||||||
previousTapZoneInvertMode.current = tapZoneInvertMode;
|
|
||||||
|
|
||||||
isInitialPreview.current = false;
|
|
||||||
}, [
|
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
areSettingsSet,
|
areSettingsSet,
|
||||||
readingMode.value,
|
readingMode,
|
||||||
readingMode.isDefault,
|
tapZoneLayout,
|
||||||
tapZoneLayout.value,
|
tapZoneInvertMode,
|
||||||
tapZoneLayout.isDefault,
|
shouldShowReadingModePreview,
|
||||||
tapZoneInvertMode.value,
|
shouldShowTapZoneLayoutPreview,
|
||||||
tapZoneInvertMode.isDefault,
|
setShowPreview,
|
||||||
]);
|
);
|
||||||
|
useReaderSetChaptersState(
|
||||||
// set chapters state
|
chaptersResponse,
|
||||||
useEffect(() => {
|
chapterSourceOrder,
|
||||||
const newMangaChapters = chaptersResponse.data?.chapters.nodes;
|
initialChapter,
|
||||||
const newCurrentChapter = newMangaChapters
|
setReaderStateChapters,
|
||||||
? (newMangaChapters[newMangaChapters.length - chapterSourceOrder] ?? null)
|
shouldSkipDupChapters,
|
||||||
: 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, chapterSourceOrder, shouldSkipDupChapters]);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setOverride({
|
setOverride({
|
||||||
|
|||||||
Reference in New Issue
Block a user