Add "auto webtoon mode" option to reader

This commit is contained in:
schroda
2024-12-26 17:15:50 +01:00
parent cfd5b78488
commit 33e184ba18
13 changed files with 184 additions and 24 deletions

View File

@@ -844,6 +844,10 @@
}, },
"settings": { "settings": {
"active_setting": "Active setting", "active_setting": "Active setting",
"auto_webtoon_mode": {
"description": "Automatically use webtoon mode for entries that are detected to likely use the long strip format",
"title": "Auto webtoon mode"
},
"background_color": "Background color", "background_color": "Background color",
"custom_filter": { "custom_filter": {
"brightness": "Custom brightness", "brightness": "Custom brightness",

View File

@@ -7,6 +7,7 @@
*/ */
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { SOURCE_BASE_FIELDS } from '@/lib/graphql/fragments/SourceFragments.ts';
export const MANGA_META_FIELDS = gql` export const MANGA_META_FIELDS = gql`
fragment MANGA_META_FIELDS on MangaMetaType { fragment MANGA_META_FIELDS on MangaMetaType {
@@ -75,10 +76,16 @@ export const MANGA_CHAPTER_NODE_FIELDS = gql`
export const MANGA_READER_FIELDS = gql` export const MANGA_READER_FIELDS = gql`
${MANGA_BASE_FIELDS} ${MANGA_BASE_FIELDS}
${MANGA_META_FIELDS} ${MANGA_META_FIELDS}
${SOURCE_BASE_FIELDS}
fragment MANGA_READER_FIELDS on MangaType { fragment MANGA_READER_FIELDS on MangaType {
...MANGA_BASE_FIELDS ...MANGA_BASE_FIELDS
genre
source {
...SOURCE_BASE_FIELDS
}
meta { meta {
...MANGA_META_FIELDS ...MANGA_META_FIELDS
} }

View File

@@ -2801,7 +2801,7 @@ export type MangaChapterStatFieldsFragment = { __typename?: 'MangaType', id: num
export type MangaChapterNodeFieldsFragment = { __typename?: 'MangaType', firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }; export type MangaChapterNodeFieldsFragment = { __typename?: 'MangaType', firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null };
export type MangaReaderFieldsFragment = { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number } }; export type MangaReaderFieldsFragment = { __typename?: 'MangaType', genre: Array<string>, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string } | null, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number } };
export type MangaLibraryFieldsFragment = { __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }; export type MangaLibraryFieldsFragment = { __typename?: 'MangaType', genre: Array<string>, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null };
@@ -3441,7 +3441,7 @@ export type GetMangaReaderQueryVariables = Exact<{
}>; }>;
export type GetMangaReaderQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number } } }; export type GetMangaReaderQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', genre: Array<string>, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string } | null, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number } } };
export type GetMangaTrackRecordsQueryVariables = Exact<{ export type GetMangaTrackRecordsQueryVariables = Exact<{
id: Scalars['Int']['input']; id: Scalars['Int']['input'];

View File

@@ -7,11 +7,11 @@
*/ */
import Checkbox, { CheckboxProps } from '@mui/material/Checkbox'; import Checkbox, { CheckboxProps } from '@mui/material/Checkbox';
import FormControlLabel from '@mui/material/FormControlLabel'; import FormControlLabel, { FormControlLabelProps } from '@mui/material/FormControlLabel';
import React from 'react'; import React from 'react';
interface IProps extends CheckboxProps { interface IProps extends CheckboxProps {
label?: string; label?: FormControlLabelProps['label'];
} }
export const CheckboxInput: React.FC<IProps> = ({ label, sx, ...rest }) => ( export const CheckboxInput: React.FC<IProps> = ({ label, sx, ...rest }) => (

View File

@@ -8,7 +8,7 @@
import { MangaStatus } from '@/lib/graphql/generated/graphql.ts'; import { MangaStatus } from '@/lib/graphql/generated/graphql.ts';
import { TranslationKey } from '@/Base.types.ts'; import { TranslationKey } from '@/Base.types.ts';
import { MangaAction, MangaIdInfo } from '@/modules/manga/Manga.types.ts'; import { MangaAction, MangaIdInfo, MangaType } from '@/modules/manga/Manga.types.ts';
export const FALLBACK_MANGA: MangaIdInfo = { id: -1 }; export const FALLBACK_MANGA: MangaIdInfo = { id: -1 };
@@ -101,3 +101,46 @@ export const actionToTranslationKey: {
error: 'manga.action.track.add.label.error', error: 'manga.action.track.add.label.error',
}, },
}; };
export const SOURCES_BY_MANGA_TYPE: Record<MangaType, string[]> = {
[MangaType.MANGA]: [],
[MangaType.COMIC]: [],
[MangaType.WEBTOON]: ['mangatoon', 'manmanga', 'toomics', 'webcomics', 'webtoons', 'webtoon'],
[MangaType.MANHWA]: [
'hiperdex',
'hmanhwa',
'instamanhwa',
'manhwa18',
'manhwa68',
'manhwa365',
'manhwahentaime',
'manhwamanga',
'manhwatop',
'manhwa',
'manytoon',
'manwha',
'readmanhwa',
'skymanga',
'toonily',
'webtoonxyz',
],
[MangaType.MANHUA]: [
'1st kiss manhua',
'hero manhua',
'manhuabox',
'manhuaus',
'manhuas',
'manhuas',
'readmanhua',
'wuxiaworld',
'manhua',
],
};
export const MANGA_TAGS_BY_MANGA_TYPE: Record<MangaType, string[]> = {
[MangaType.MANGA]: ['manga'],
[MangaType.COMIC]: ['comic'],
[MangaType.WEBTOON]: ['webtoon', 'long strip'],
[MangaType.MANHWA]: ['manhwa'],
[MangaType.MANHUA]: ['manhua'],
};

View File

@@ -10,27 +10,36 @@ import { LongPressPointerHandlers, LongPressResult } from 'use-long-press/lib/us
import { PopupState } from 'material-ui-popup-state/hooks'; import { PopupState } from 'material-ui-popup-state/hooks';
import { SelectableCollectionReturnType } from '@/modules/collection/hooks/useSelectableCollection.ts'; import { SelectableCollectionReturnType } from '@/modules/collection/hooks/useSelectableCollection.ts';
import { useManageMangaLibraryState } from '@/modules/manga/hooks/useManageMangaLibraryState.tsx'; import { useManageMangaLibraryState } from '@/modules/manga/hooks/useManageMangaLibraryState.tsx';
import { ChapterType, MangaReaderFieldsFragment, MangaType, TrackRecordType } from '@/lib/graphql/generated/graphql.ts'; import {
ChapterType,
MangaReaderFieldsFragment,
MangaType as MangaTypeGql,
Maybe,
SourceType,
TrackRecordType,
} from '@/lib/graphql/generated/graphql.ts';
import { SingleModeProps } from '@/modules/manga/components/MangaActionMenuItems.tsx'; import { SingleModeProps } from '@/modules/manga/components/MangaActionMenuItems.tsx';
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
import { GridLayout } from '@/modules/core/Core.types.ts'; import { GridLayout } from '@/modules/core/Core.types.ts';
export type MangaCardMode = 'default' | 'source' | 'migrate.search' | 'migrate.select' | 'duplicate'; export type MangaCardMode = 'default' | 'source' | 'migrate.search' | 'migrate.select' | 'duplicate';
type MangaCardBaseProps = Pick<MangaType, 'id' | 'title' | 'sourceId'> & type MangaCardBaseProps = Pick<MangaTypeGql, 'id' | 'title' | 'sourceId'> &
Omit<SingleModeProps['manga'], 'downloadCount' | 'unreadCount' | 'chapters'> & Omit<SingleModeProps['manga'], 'downloadCount' | 'unreadCount' | 'chapters'> &
Partial<Pick<MangaType, 'inLibrary' | 'downloadCount' | 'unreadCount'>> & { Partial<Pick<MangaTypeGql, 'inLibrary' | 'downloadCount' | 'unreadCount'>> & {
firstUnreadChapter?: Pick<ChapterType, 'id' | 'sourceOrder' | 'isRead'> | null; firstUnreadChapter?: Pick<ChapterType, 'id' | 'sourceOrder' | 'isRead'> | null;
}; };
export type MangaIdInfo = Pick<MangaType, 'id'>; export type MangaIdInfo = Pick<MangaTypeGql, 'id'>;
export type MangaChapterCountInfo = { chapters: Pick<MangaType['chapters'], 'totalCount'> }; export type MangaChapterCountInfo = { chapters: Pick<MangaTypeGql['chapters'], 'totalCount'> };
export type MangaDownloadInfo = Pick<MangaType, 'downloadCount'> & MangaChapterCountInfo; export type MangaDownloadInfo = Pick<MangaTypeGql, 'downloadCount'> & MangaChapterCountInfo;
export type MangaUnreadInfo = Pick<MangaType, 'unreadCount'> & MangaChapterCountInfo; export type MangaUnreadInfo = Pick<MangaTypeGql, 'unreadCount'> & MangaChapterCountInfo;
export type MangaThumbnailInfo = Pick<MangaType, 'thumbnailUrl' | 'thumbnailUrlLastFetched'>; export type MangaThumbnailInfo = Pick<MangaTypeGql, 'thumbnailUrl' | 'thumbnailUrlLastFetched'>;
export type MangaTrackRecordInfo = MangaIdInfo & { export type MangaTrackRecordInfo = MangaIdInfo & {
trackRecords: { nodes: Pick<TrackRecordType, 'id' | 'trackerId'>[] }; trackRecords: { nodes: Pick<TrackRecordType, 'id' | 'trackerId'>[] };
}; };
export type MangaGenreInfo = Pick<MangaTypeGql, 'genre'>;
export type MangaSourceNameInfo = { source?: Maybe<Pick<SourceType, 'name'>> };
export type MigrateMode = 'copy' | 'migrate'; export type MigrateMode = 'copy' | 'migrate';
@@ -41,7 +50,7 @@ export interface MangaCardProps {
gridLayout?: GridLayout; gridLayout?: GridLayout;
inLibraryIndicator?: boolean; inLibraryIndicator?: boolean;
selected?: boolean | null; selected?: boolean | null;
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection']; handleSelection?: SelectableCollectionReturnType<MangaTypeGql['id']>['handleSelection'];
mode?: MangaCardMode; mode?: MangaCardMode;
} }
@@ -69,3 +78,11 @@ export type MangaAction =
| 'track'; | 'track';
export type TMangaReader = MangaReaderFieldsFragment; export type TMangaReader = MangaReaderFieldsFragment;
export enum MangaType {
MANGA,
COMIC,
WEBTOON,
MANHWA,
MANHUA,
}

View File

@@ -28,12 +28,19 @@ import { MetadataMigrationSettings } from '@/modules/migration/Migration.types.t
import { import {
MangaAction, MangaAction,
MangaDownloadInfo, MangaDownloadInfo,
MangaGenreInfo,
MangaIdInfo, MangaIdInfo,
MangaSourceNameInfo,
MangaThumbnailInfo, MangaThumbnailInfo,
MangaType,
MangaUnreadInfo, MangaUnreadInfo,
MigrateMode, MigrateMode,
} from '@/modules/manga/Manga.types.ts'; } from '@/modules/manga/Manga.types.ts';
import { actionToTranslationKey } from '@/modules/manga/Manga.constants.ts'; import {
actionToTranslationKey,
MANGA_TAGS_BY_MANGA_TYPE,
SOURCES_BY_MANGA_TYPE,
} from '@/modules/manga/Manga.constants.ts';
import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts';
type MangaToMigrate = NonNullable<GetMangaToMigrateQuery['manga']>; type MangaToMigrate = NonNullable<GetMangaToMigrateQuery['manga']>;
@@ -524,4 +531,43 @@ export class Mangas {
throw new Error(`Mangas::performAction: unknown action "${action}"`); throw new Error(`Mangas::performAction: unknown action "${action}"`);
} }
} }
static getType(manga: MangaGenreInfo & MangaSourceNameInfo): MangaType {
if (Mangas.isType(manga, MangaType.MANGA)) {
return MangaType.MANGA;
}
if (Mangas.isType(manga, MangaType.COMIC)) {
return MangaType.COMIC;
}
if (Mangas.isType(manga, MangaType.WEBTOON)) {
return MangaType.WEBTOON;
}
if (Mangas.isType(manga, MangaType.MANHWA)) {
return MangaType.MANHWA;
}
if (Mangas.isType(manga, MangaType.MANHUA)) {
return MangaType.MANHUA;
}
return MangaType.MANGA;
}
static isType(manga: MangaGenreInfo & MangaSourceNameInfo, type: MangaType): boolean {
return (
manga.genre.some((genre) => MANGA_TAGS_BY_MANGA_TYPE[type].includes(genre.toLowerCase())) ||
SOURCES_BY_MANGA_TYPE[type].includes(manga.source?.name.toLowerCase() ?? '')
);
}
static isLongStripType(manga: MangaGenreInfo & MangaSourceNameInfo): boolean {
return (
Mangas.isType(manga, MangaType.WEBTOON) ||
Mangas.isType(manga, MangaType.MANHWA) ||
Mangas.isType(manga, MangaType.MANHUA)
);
}
} }

View File

@@ -68,6 +68,7 @@ const APP_METADATA_OBJECT: Record<AppMetadataKeys, undefined> = {
pageGap: undefined, pageGap: undefined,
hotkeys: undefined, hotkeys: undefined,
imagePreLoadAmount: undefined, imagePreLoadAmount: undefined,
shouldUseAutoWebtoonMode: undefined,
}; };
export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA_OBJECT); export const VALID_APP_METADATA_KEYS = Object.keys(APP_METADATA_OBJECT);

View File

@@ -8,6 +8,8 @@
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { IReaderSettingsWithDefaultFlag, ReaderSettingsTypeProps } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettingsWithDefaultFlag, ReaderSettingsTypeProps } from '@/modules/reader/types/Reader.types.ts';
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx'; import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
@@ -46,6 +48,18 @@ export const ReaderBehaviourSettings = ({
onChange={(_, checked) => updateSetting('shouldOffsetDoubleSpreads', checked)} onChange={(_, checked) => updateSetting('shouldOffsetDoubleSpreads', checked)}
/> />
)} )}
<CheckboxInput
label={
<Box>
<Typography>{t('reader.settings.auto_webtoon_mode.title')}</Typography>
<Typography variant="body2" color="textDisabled">
{t('reader.settings.auto_webtoon_mode.description')}
</Typography>
</Box>
}
checked={settings.shouldUseAutoWebtoonMode}
onChange={(_, checked) => updateSetting('shouldUseAutoWebtoonMode', checked)}
/>
<SliderInput <SliderInput
label={t('reader.settings.image_preload_amount')} label={t('reader.settings.image_preload_amount')}
value={settings.imagePreLoadAmount} value={settings.imagePreLoadAmount}

View File

@@ -55,6 +55,7 @@ const GLOBAL_READER_SETTING_OBJECT: Record<keyof IReaderSettingsGlobal, undefine
backgroundColor: undefined, backgroundColor: undefined,
hotkeys: undefined, hotkeys: undefined,
imagePreLoadAmount: undefined, imagePreLoadAmount: undefined,
shouldUseAutoWebtoonMode: undefined,
}; };
export const GLOBAL_READER_SETTING_KEYS = Object.keys(GLOBAL_READER_SETTING_OBJECT); export const GLOBAL_READER_SETTING_KEYS = Object.keys(GLOBAL_READER_SETTING_OBJECT);
@@ -123,6 +124,7 @@ export const DEFAULT_READER_SETTINGS: IReaderSettings = {
[ReaderHotkey.CYCLE_READING_DIRECTION]: ['t'], [ReaderHotkey.CYCLE_READING_DIRECTION]: ['t'],
}, },
imagePreLoadAmount: 5, imagePreLoadAmount: 5,
shouldUseAutoWebtoonMode: true,
}; };
export const READER_PROGRESS_BAR_POSITION_TO_PLACEMENT: Record<ProgressBarPosition, TooltipProps['placement']> = { export const READER_PROGRESS_BAR_POSITION_TO_PLACEMENT: Record<ProgressBarPosition, TooltipProps['placement']> = {

View File

@@ -42,6 +42,7 @@ import {
ReaderResumeMode, ReaderResumeMode,
ReaderStateChapters, ReaderStateChapters,
ReaderTransitionPageMode, ReaderTransitionPageMode,
ReadingMode,
TReaderStateMangaContext, TReaderStateMangaContext,
TReaderStateSettingsContext, TReaderStateSettingsContext,
} from '@/modules/reader/types/Reader.types.ts'; } from '@/modules/reader/types/Reader.types.ts';
@@ -52,7 +53,7 @@ import { TReaderOverlayContext } from '@/modules/reader/types/ReaderOverlay.type
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 { FALLBACK_MANGA } from '@/modules/manga/Manga.constants.ts'; import { isAutoWebtoonMode } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
const BaseReader = ({ const BaseReader = ({
setTitle, setTitle,
@@ -221,25 +222,36 @@ const BaseReader = ({
// set settings state // set settings state
useEffect(() => { useEffect(() => {
if (!mangaResponse.data?.manga || defaultSettingsResponse.loading || defaultSettingsResponse.error) { const mangaFromResponse = mangaResponse.data?.manga;
if (!mangaFromResponse || defaultSettingsResponse.loading || defaultSettingsResponse.error) {
return; return;
} }
const settingsWithDefaultProfileFallback = getReaderSettingsFor( const settingsWithDefaultProfileFallback = getReaderSettingsFor(mangaFromResponse, defaultSettings);
mangaResponse.data?.manga ?? FALLBACK_MANGA,
defaultSettings, const shouldUseWebtoonMode = isAutoWebtoonMode(
mangaFromResponse,
settingsWithDefaultProfileFallback.shouldUseAutoWebtoonMode,
settingsWithDefaultProfileFallback.readingMode,
); );
const profile = settingsWithDefaultProfileFallback.readingMode.value; const defaultSettingsWithAutoReadingMode = {
...defaultSettings,
readingMode: shouldUseWebtoonMode ? ReadingMode.WEBTOON : defaultSettings.readingMode,
};
const profile = shouldUseWebtoonMode
? ReadingMode.WEBTOON
: settingsWithDefaultProfileFallback.readingMode.value;
const profileSettings = getReaderSettings( const profileSettings = getReaderSettings(
'global', 'global',
{ meta: defaultSettingsMetadata! }, { meta: defaultSettingsMetadata! },
defaultSettings, defaultSettingsWithAutoReadingMode,
undefined, undefined,
profile, profile,
); );
setSettings(getReaderSettingsFor(mangaResponse.data!.manga, profileSettings)); setSettings(getReaderSettingsFor(mangaFromResponse, profileSettings));
}, [mangaResponse.data?.manga, defaultSettings]); }, [mangaResponse.data?.manga, defaultSettings]);
// set chapters state // set chapters state

View File

@@ -123,6 +123,7 @@ export interface IReaderSettingsGlobal {
backgroundColor: ReaderBackgroundColor; backgroundColor: ReaderBackgroundColor;
hotkeys: Record<ReaderHotkey, string[]>; hotkeys: Record<ReaderHotkey, string[]>;
imagePreLoadAmount: number; imagePreLoadAmount: number;
shouldUseAutoWebtoonMode: boolean;
} }
export interface IReaderSettingsManga { export interface IReaderSettingsManga {

View File

@@ -6,7 +6,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { IReaderSettings, ReaderPageScaleMode, ReadingMode } from '@/modules/reader/types/Reader.types.ts'; import {
IReaderSettings,
IReaderSettingsWithDefaultFlag,
ReaderPageScaleMode,
ReadingMode,
} from '@/modules/reader/types/Reader.types.ts';
import { MangaGenreInfo, MangaSourceNameInfo } from '@/modules/manga/Manga.types.ts';
import { Mangas } from '@/modules/manga/services/Mangas.ts';
export const isOffsetDoubleSpreadPagesEditable = (readingMode: IReaderSettings['readingMode']): boolean => export const isOffsetDoubleSpreadPagesEditable = (readingMode: IReaderSettings['readingMode']): boolean =>
readingMode === ReadingMode.DOUBLE_PAGE; readingMode === ReadingMode.DOUBLE_PAGE;
@@ -19,3 +26,9 @@ export const isContinuousReadingMode = (readingMode: IReaderSettings['readingMod
export const isContinuousVerticalReadingMode = (readingMode: IReaderSettings['readingMode']): boolean => export const isContinuousVerticalReadingMode = (readingMode: IReaderSettings['readingMode']): boolean =>
[ReadingMode.CONTINUOUS_VERTICAL, ReadingMode.WEBTOON].includes(readingMode); [ReadingMode.CONTINUOUS_VERTICAL, ReadingMode.WEBTOON].includes(readingMode);
export const isAutoWebtoonMode = (
manga: MangaGenreInfo & MangaSourceNameInfo,
shouldUseAutoWebtoonMode: IReaderSettings['shouldUseAutoWebtoonMode'],
readingMode: IReaderSettingsWithDefaultFlag['readingMode'],
): boolean => shouldUseAutoWebtoonMode && readingMode.isDefault && Mangas.isLongStripType(manga);