diff --git a/src/features/manga/components/MangaActionMenuItems.tsx b/src/features/manga/components/MangaActionMenuItems.tsx index 8cc7ea47..60d53281 100644 --- a/src/features/manga/components/MangaActionMenuItems.tsx +++ b/src/features/manga/components/MangaActionMenuItems.tsx @@ -15,7 +15,6 @@ import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; import Label from '@mui/icons-material/Label'; import { useMemo, useState } from 'react'; import SyncAltIcon from '@mui/icons-material/SyncAlt'; -import { Link } from 'react-router-dom'; import SyncIcon from '@mui/icons-material/Sync'; import Dialog from '@mui/material/Dialog'; import { AwaitableComponent } from 'awaitable-component'; @@ -35,7 +34,6 @@ import { NestedMenuItem } from '@/base/components/menu/NestedMenuItem.tsx'; import type { MangaChapterStatFieldsFragment, MangaType } from '@/lib/graphql/generated/graphql.ts'; import type { MangaAction, MangaDownloadInfo, MangaIdInfo, MangaUnreadInfo } from '@/features/manga/Manga.types.ts'; import { MANGA_ACTION_TO_TRANSLATION } from '@/features/manga/Manga.constants.ts'; -import { AppRoutes } from '@/base/AppRoute.constants.ts'; import { CategorySelect } from '@/features/category/components/CategorySelect.tsx'; import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; @@ -57,7 +55,7 @@ type Props = export const MangaActionMenuItems = ({ manga, handleSelection, - selectedMangas: passedSelectedMangas, + selectedMangas = STABLE_EMPTY_ARRAY, onClose, setHideMenu, }: Props) => { @@ -66,7 +64,6 @@ export const MangaActionMenuItems = ({ const [isTrackDialogOpen, setIsTrackDialogOpen] = useState(false); const isSingleMode = !!manga; - const selectedMangas = passedSelectedMangas ?? STABLE_EMPTY_ARRAY; const getMenuItemTitle = createGetMenuItemTitle(isSingleMode, MANGA_ACTION_TO_TRANSLATION); const shouldShowMenuItem = createShouldShowMenuItem(isSingleMode); @@ -148,19 +145,11 @@ export const MangaActionMenuItems = ({ title={getMenuItemTitle('mark_as_unread', readMangas.length)} /> )} - {isSingleMode && ( - - - - )} + performAction('migrate', selectedMangas)} + /> {isSingleMode && ( { @@ -175,7 +164,7 @@ export const MangaActionMenuItems = ({ onClick={() => { AwaitableComponent.show(CategorySelect, { mangaId: manga?.id, - mangaIds: passedSelectedMangas ? Mangas.getIds(selectedMangas) : undefined, + mangaIds: selectedMangas ? Mangas.getIds(selectedMangas) : undefined, addToLibrary: false, }); setHideMenu(true); diff --git a/src/features/manga/components/cards/MangaCard.tsx b/src/features/manga/components/cards/MangaCard.tsx index 07f1192f..df8f116f 100644 --- a/src/features/manga/components/cards/MangaCard.tsx +++ b/src/features/manga/components/cards/MangaCard.tsx @@ -22,13 +22,15 @@ import { GridLayout } from '@/base/Base.types.ts'; import { AppRoutes } from '@/base/AppRoute.constants.ts'; import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; import { makeToast } from '@/base/utils/Toast.ts'; -import { Mangas } from '@/features/manga/services/Mangas.ts'; import { t } from '@lingui/core/macro'; import { useParams } from 'react-router-dom'; import { ReactRouter } from '@/lib/react-router/ReactRouter.ts'; import { MigrationOptionsDialog } from '@/features/migration/components/MigrationOptionsDialog.tsx'; import { AwaitableComponent } from 'awaitable-component'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler'; +import { MangaMigration } from '@/features/migration/MangaMigration.ts'; +import { MANGA_ACTION_TO_TRANSLATION } from '@/features/manga/Manga.constants.ts'; +import { getErrorMessage } from '@/lib/HelperFunctions.ts'; const getMangaLinkTo = ( mode: MangaCardMode, @@ -123,8 +125,15 @@ export const MangaCard = memo((props: MangaCardProps) => { optionsDialog.update({ isMigrating: true }); try { - await Mangas.migrate(migrationSourceMangaId, id, options); - + try { + await MangaMigration.migrate(migrationSourceMangaId, id, options); + } catch (e) { + makeToast( + t(MANGA_ACTION_TO_TRANSLATION['migrate'].error), + 'error', + getErrorMessage(e), + ); + } optionsDialog.submit(options); ReactRouter.navigate(AppRoutes.manga.path(id), { replace: true }); diff --git a/src/features/manga/services/Mangas.ts b/src/features/manga/services/Mangas.ts index 96f9da5e..5a963984 100644 --- a/src/features/manga/services/Mangas.ts +++ b/src/features/manga/services/Mangas.ts @@ -22,8 +22,7 @@ import { Chapters } from '@/features/chapter/services/Chapters.ts'; import { makeToast } from '@/base/utils/Toast.ts'; import { getMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; import { GET_MANGAS_BASE } from '@/lib/graphql/manga/MangaQuery.ts'; -import { MANGA_BASE_FIELDS } from '@/lib/graphql/manga/MangaFragments.ts'; -import type { MigrateOptions } from '@/features/migration/Migration.types.ts'; +import { MANGA_BASE_FIELDS, MANGA_MIGRATION_FIELDS } from '@/lib/graphql/manga/MangaFragments.ts'; import type { MangaAction, MangaArtistInfo, @@ -49,7 +48,10 @@ import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { assertIsDefined } from '@/base/Asserts.ts'; import { Confirmation } from '@/base/AppAwaitableComponent.ts'; import { UrlUtil } from '@/lib/UrlUtil.ts'; -import { MangaMigration } from '@/features/migration/MangaMigration.ts'; +import { MigrationManager } from '@/features/migration/MigrationManager.ts'; +import uniq from 'lodash/fp/uniq'; +import { ReactRouter } from '@/lib/react-router/ReactRouter.ts'; +import { AppRoutes } from '@/base/AppRoute.constants.ts'; type DownloadChaptersOptions = { size?: number; @@ -61,23 +63,18 @@ type ChangeCategoriesOptions = { changeCategoriesPatch: UpdateMangaCategoriesPat type MarkAsReadActionOption = MarkAsReadOptions & PropertiesNever & - PropertiesNever & PropertiesNever; type ChangeCategoriesActionOption = PropertiesNever & ChangeCategoriesOptions & - PropertiesNever & PropertiesNever; type MigrateActionOption = PropertiesNever & PropertiesNever & - MigrateOptions & PropertiesNever; type DownloadActionOption = PropertiesNever & PropertiesNever & - PropertiesNever & DownloadChaptersOptions; type DefaultActionOption = Partial & Partial & - Partial & Partial; type PerformActionOptions = Action extends 'mark_as_read' @@ -298,18 +295,22 @@ export class Mangas { ); } - static async migrate( - mangaId: MangaIdInfo['id'], - mangaIdToMigrateTo: number, - options: Omit, - disableConfirmation?: boolean, - ): Promise { - return Mangas.executeAction( - 'migrate', - 1, - () => MangaMigration.migrate(mangaId, mangaIdToMigrateTo, options), - disableConfirmation, - ); + static async migrate(mangaIds: MangaIdInfo['id'][]): Promise { + const mangas = mangaIds.map((mangaId) => { + const manga = Mangas.getFromCache(mangaId, MANGA_MIGRATION_FIELDS, 'MANGA_MIGRATION_FIELDS'); + assertIsDefined(manga); + + return manga; + }); + const sourceIds = uniq(mangas.map((manga) => manga.sourceId)); + + MigrationManager.selectSources(sourceIds); + MigrationManager.selectMangas(mangas); + + const isBulkMigration = mangas.length > 1; + if (isBulkMigration) { + ReactRouter.navigate(AppRoutes.migrate.path); + } } private static async executeAction( @@ -367,11 +368,9 @@ export class Mangas { { wasManuallyMarkedAsRead, changeCategoriesPatch, - mangaIdToMigrateTo, downloadAhead, onlyUnread, size, - ...migrateOptions }: PerformActionOptions, disableConfirmation?: boolean, ): Promise { @@ -389,12 +388,7 @@ export class Mangas { case 'change_categories': return Mangas.changeCategories(mangaIds, changeCategoriesPatch!, disableConfirmation); case 'migrate': { - return Mangas.migrate( - mangaIds[0], - mangaIdToMigrateTo!, - migrateOptions as unknown as MigrateOptions, - disableConfirmation, - ); + return Mangas.migrate(mangaIds); } default: throw new Error(`Mangas::performAction: unknown action "${action}"`); diff --git a/src/features/migration/Migration.constants.ts b/src/features/migration/Migration.constants.ts index 180e0b79..2b120f79 100644 --- a/src/features/migration/Migration.constants.ts +++ b/src/features/migration/Migration.constants.ts @@ -34,7 +34,7 @@ export const MAX_SOURCES_IN_PARALLEL = 6; export const DEFAULT_MIGRATION_STATE: MigrationState = { phase: MigrationPhase.IDLE, - sourceId: null, + sourceIds: null, entries: {}, destinationSourceIds: [], migrateOptions: null, diff --git a/src/features/migration/Migration.types.ts b/src/features/migration/Migration.types.ts index 95f3bbd5..996a520d 100644 --- a/src/features/migration/Migration.types.ts +++ b/src/features/migration/Migration.types.ts @@ -111,7 +111,7 @@ export type MigrationProgress = { total: number; completed: number; success: num export interface MigrationState { phase: MigrationPhase; - sourceId: SourceIdInfo['id'] | null; + sourceIds: SourceIdInfo['id'][] | null; entries: Record; destinationSourceIds: SourceIdInfo['id'][]; migrateOptions: Omit | null; @@ -123,3 +123,7 @@ export interface MigrationState { } export interface SourceItem extends SourceIdInfo, SourceNameInfo, SourceLanguageInfo, SourceIconInfo, SourceMetaInfo {} + +export type TMigratableSource = NonNullable & { + mangaCount: number; +}; diff --git a/src/features/migration/MigrationManager.ts b/src/features/migration/MigrationManager.ts index 18dda2ff..3e17cf1f 100644 --- a/src/features/migration/MigrationManager.ts +++ b/src/features/migration/MigrationManager.ts @@ -14,11 +14,11 @@ import { devtools, persist } from 'zustand/middleware'; import { type MigratableEntry, type MigrateOptions, - type TMigrationEntry, MigrationEntryStatus, - MigrationPhase, type MigrationMatch, + MigrationPhase, type MigrationState, + type TMigrationEntry, } from '@/features/migration/Migration.types.ts'; import { DEFAULT_MIGRATION_STATE, @@ -153,6 +153,14 @@ export class MigrationManager { } } + private static ensureIsInValidPhase(phases: MigrationPhase[]): void { + const { phase } = MigrationManager.getState(); + + if (!phases.includes(phase)) { + throw new Error(`Illegal migration phase "${phase}". Expected: ${phases}`); + } + } + static async goToPreviousPhase(): Promise { switch (MigrationManager.getState().phase) { case MigrationPhase.IDLE: @@ -265,16 +273,34 @@ export class MigrationManager { return updatedEntry; } - static selectSource(sourceId: SourceIdInfo['id']): void { + static selectSources(sourceIds: SourceIdInfo['id'][]): void { + MigrationManager.ensureIsInValidPhase([MigrationPhase.IDLE]); + MigrationManager.updateState((draft) => { draft.phase = MigrationPhase.SELECT_MANGAS; - draft.sourceId = sourceId; + draft.sourceIds = sourceIds; draft.entries = {}; }); - ReactRouter.navigate(AppRoutes.migrate.path); } static selectMangas(mangas: MangaMigrationFieldsFragment[]): void { + MigrationManager.ensureIsInValidPhase([MigrationPhase.SELECT_MANGAS]); + + const isSingleManga = mangas.length === 1; + if (isSingleManga) { + const [manga] = mangas; + + ReactRouter.navigate( + AppRoutes.migrate.childRoutes.singleMangaSearch.path(manga.sourceId, manga.id, manga.title), + { + state: { mangaTitle: manga.title }, + }, + ); + + MigrationManager.reset(); + return; + } + MigrationManager.updateState((draft) => { draft.phase = MigrationPhase.SELECTING_SOURCES; draft.entries = Object.fromEntries( @@ -305,6 +331,8 @@ export class MigrationManager { } static async startSearch(destinationSourceIds: SourceIdInfo['id'][]): Promise { + MigrationManager.ensureIsInValidPhase([MigrationPhase.SELECTING_SOURCES]); + MigrationManager.updateState((draft) => { draft.destinationSourceIds = destinationSourceIds; }); @@ -369,6 +397,8 @@ export class MigrationManager { } static async startMigration(options: Omit): Promise { + MigrationManager.ensureIsInValidPhase([MigrationPhase.SEARCHING]); + const migratableEntries = MigrationManager.getMigratableEntries(); await Confirmation.show({ @@ -551,25 +581,49 @@ export class MigrationManager { return RESUMABLE_PHASES.includes(MigrationManager.getState().phase); } - private static getHigherPrioritySourceIds(sourceId: SourceIdInfo['id']): SourceIdInfo['id'][] { - const { destinationSourceIds } = MigrationManager.getState(); + private static getDestinationSourceIds(mangaSourceId: SourceIdInfo['id']): MigrationState['destinationSourceIds'] { + const { sourceIds, destinationSourceIds } = MigrationManager.getState(); - const sourceIdPriority = destinationSourceIds.indexOf(sourceId); + // Prevent (most likely) unintentionally matching a manga to itself. + const isMultiSourceMigration = (sourceIds?.length ?? -1) > 1; + if (isMultiSourceMigration) { + const mangaSourceIdIndex = destinationSourceIds.indexOf(mangaSourceId); + + return [ + ...destinationSourceIds.slice(0, mangaSourceIdIndex), + ...destinationSourceIds.slice(mangaSourceIdIndex + 1), + mangaSourceId, + ]; + } + + return destinationSourceIds; + } + + private static getHigherPrioritySourceIds( + mangaSourceId: SourceIdInfo['id'], + destSourceId: SourceIdInfo['id'], + ): SourceIdInfo['id'][] { + const destinationSourceIds = MigrationManager.getDestinationSourceIds(mangaSourceId); + + const sourceIdPriority = destinationSourceIds.indexOf(destSourceId); return destinationSourceIds.slice(0, Math.max(0, sourceIdPriority - 1)); } - private static isHigherPrioritySourceUnsettled(mangaId: MangaIdInfo['id'], sourceId: SourceIdInfo['id']): boolean { + private static isHigherPrioritySourceUnsettled( + mangaId: MangaIdInfo['id'], + destSourceId: SourceIdInfo['id'], + ): boolean { const { entries } = MigrationManager.getState(); const entry = entries[mangaId]; assertIsDefined(entry); - return MigrationManager.getHigherPrioritySourceIds(sourceId).some( + return MigrationManager.getHigherPrioritySourceIds(entry.sourceId, destSourceId).some( (higherPrioritySourceId) => entry.destSourceIdToSearchState[higherPrioritySourceId] == null, ); } - private static hasHigherSourcePriorityMatch(mangaId: MangaIdInfo['id'], sourceId: SourceIdInfo['id']): boolean { + private static hasHigherSourcePriorityMatch(mangaId: MangaIdInfo['id'], destSourceId: SourceIdInfo['id']): boolean { const { entries } = MigrationManager.getState(); const entry = entries[mangaId]; @@ -577,7 +631,7 @@ export class MigrationManager { return false; } - return MigrationManager.getHigherPrioritySourceIds(sourceId).some( + return MigrationManager.getHigherPrioritySourceIds(entry.sourceId, destSourceId).some( (higherPrioritySourceId) => entry.destSourceIdToSearchState[higherPrioritySourceId], ); } @@ -658,7 +712,7 @@ export class MigrationManager { }); try { - const searchPromises = state.destinationSourceIds.map((destSourceId) => + const searchPromises = MigrationManager.getDestinationSourceIds(entry.sourceId).map((destSourceId) => MigrationManager.getParallelSourceQueue()(async () => { if (signal.aborted) { return null; @@ -893,8 +947,8 @@ export class MigrationManager { return useMigrationStore((state) => state.phase); } - static useSourceId(): SourceIdInfo['id'] | null { - return useMigrationStore((state) => state.sourceId); + static useSourceIds(): MigrationState['sourceIds'] { + return useMigrationStore((state) => state.sourceIds); } static useEntries(): Record { diff --git a/src/features/migration/components/MigrationCard.tsx b/src/features/migration/components/MigrationCard.tsx index fb752611..64f7cfd6 100644 --- a/src/features/migration/components/MigrationCard.tsx +++ b/src/features/migration/components/MigrationCard.tsx @@ -13,26 +13,29 @@ import Chip from '@mui/material/Chip'; import Typography from '@mui/material/Typography'; import { useLingui } from '@lingui/react/macro'; import { requestManager } from '@/lib/requests/RequestManager.ts'; -import type { GetMigratableSourcesQuery } from '@/lib/graphql/generated/graphql.ts'; import { translateExtensionLanguage } from '@/features/extension/Extensions.utils.ts'; import { MigrationManager } from '@/features/migration/MigrationManager.ts'; import { ListCardAvatar } from '@/base/components/lists/cards/ListCardAvatar.tsx'; import { ListCardContent } from '@/base/components/lists/cards/ListCardContent.tsx'; +import { Sources } from '@/features/source/services/Sources'; +import type { TMigratableSource } from '@/features/migration/Migration.types.ts'; +import { ReactRouter } from '@/lib/react-router/ReactRouter.ts'; +import { AppRoutes } from '@/base/AppRoute.constants.ts'; -export type TMigratableSource = NonNullable & { - mangaCount: number; -}; - -// TODO - cleanup source/extension components -export const MigrationCard = ({ id, name, lang, iconUrl, mangaCount }: TMigratableSource) => { +export const MigrationCard = (source: TMigratableSource) => { + const { id, name, lang, iconUrl, mangaCount } = source; const { t } = useLingui(); - const isLocalSource = Number(id) === 0; - const sourceName = isLocalSource ? t`Local source` : name; + const sourceName = Sources.isLocalSource(source) ? t`Local source` : name; return ( - MigrationManager.selectSource(id)}> + { + MigrationManager.selectSources([id]); + ReactRouter.navigate(AppRoutes.migrate.path); + }} + > ['handleSelection']; handlePriorityChange: (oldIndex: number, newIndex: number) => void; selectedSourceIds: SourceIdInfo['id'][]; - currentSourceId: SourceIdInfo['id'] | null; + currentSourceIds: MigrationState['sourceIds']; }) => { const { t } = useLingui(); const dndSensors = DndKitUtil.useSensorsForDevice(); @@ -207,7 +207,7 @@ export const MigrationSourceList = ({ ); @@ -232,7 +232,7 @@ export const MigrationSourceList = ({ diff --git a/src/features/migration/screens/MigrationExecute.tsx b/src/features/migration/screens/MigrationExecute.tsx index 67df9f18..95befa64 100644 --- a/src/features/migration/screens/MigrationExecute.tsx +++ b/src/features/migration/screens/MigrationExecute.tsx @@ -68,7 +68,7 @@ export const MigrationExecute = () => { })} entries={migratingEntries} isMigrating - color="error" + color="info" /> { other: '# searching', })} entries={searchingEntries} - color="error" + color="info" /> { const { t } = useLingui(); - const currentSourceId = MigrationManager.useSourceId(); + const currentSourceIds = MigrationManager.useSourceIds(); const { settings: { browseLanguages, showNsfw }, @@ -156,7 +156,7 @@ export const MigrationSelectDestinationSources = () => { selectedSourceIds={selectedItemIds} handleSelection={handleSelection} handlePriorityChange={handlePriorityChange} - currentSourceId={currentSourceId} + currentSourceIds={currentSourceIds} /> { const message = getErrorMessage(error); @@ -45,9 +43,11 @@ const getSourceError = (error: unknown): unknown => { export const MigrationSelectMangas = () => { const { t } = useLingui(); - const sourceId = MigrationManager.useSourceId(); + const sourceIds = MigrationManager.useSourceIds(); const selectedMangas = MigrationManager.useEntries(); + const sourceId = sourceIds?.[0]; + const [gridLayout, setGridLayout] = useLocalStorage('migrateGridLayout', GridLayout.List); const { @@ -58,7 +58,7 @@ export const MigrationSelectMangas = () => { } = requestManager.useGetSource( GET_SOURCE_MIGRATABLE, sourceId ?? '', - { skip: !sourceId, notifyOnNetworkStatusChange: true }, + { skip: !sourceIds, notifyOnNetworkStatusChange: true }, ); const { @@ -67,7 +67,7 @@ export const MigrationSelectMangas = () => { error: mangasError, refetch: refetchMangas, } = requestManager.useGetMigratableSourceMangas(sourceId!, { - skip: !sourceId, + skip: !sourceIds, notifyOnNetworkStatusChange: true, }); @@ -86,7 +86,7 @@ export const MigrationSelectMangas = () => { ), }); - const sourceName = migratableSourceData?.source?.displayName ?? sourceId ?? t`Migrate`; + const sourceName = migratableSourceData?.source?.displayName ?? sourceIds ?? t`Migrate`; useAppTitleAndAction( sourceName, <> @@ -108,22 +108,6 @@ export const MigrationSelectMangas = () => { const handleContinue = () => { const selected = mangas.filter((manga) => selectedItemIds.includes(manga.id)); - const [entry] = selected; - const isSingleManga = selected.length === 1; - - if (isSingleManga && entry) { - ReactRouter.navigate( - AppRoutes.migrate.childRoutes.singleMangaSearch.path(entry.sourceId, entry.id, entry.title), - { - state: { mangaTitle: entry.title }, - }, - ); - - MigrationManager.reset(); - - return; - } - MigrationManager.selectMangas(selected); }; diff --git a/src/features/migration/screens/MigrationSelectSource.tsx b/src/features/migration/screens/MigrationSelectSource.tsx index 1810308f..fa14c839 100644 --- a/src/features/migration/screens/MigrationSelectSource.tsx +++ b/src/features/migration/screens/MigrationSelectSource.tsx @@ -19,11 +19,14 @@ import { CustomTooltip } from '@/base/components/CustomTooltip.tsx'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx'; import { EmptyViewAbsoluteCentered } from '@/base/components/feedback/EmptyViewAbsoluteCentered.tsx'; -import type { TMigratableSource } from '@/features/migration/components/MigrationCard.tsx'; import { MigrationCard } from '@/features/migration/components/MigrationCard.tsx'; import { StyledGroupItemWrapper } from '@/base/components/virtuoso/StyledGroupItemWrapper.tsx'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; -import type { SortSettings, TMigratableSourcesResult } from '@/features/migration/Migration.types.ts'; +import type { + SortSettings, + TMigratableSource, + TMigratableSourcesResult, +} from '@/features/migration/Migration.types.ts'; import { SortBy, SortOrder } from '@/features/migration/Migration.types.ts'; import { sortByToTranslation, sortOrderToTranslation } from '@/features/migration/Migration.constants.ts'; import { diff --git a/src/lib/graphql/generated/graphql.ts b/src/lib/graphql/generated/graphql.ts index f755de2f..a960e16b 100644 --- a/src/lib/graphql/generated/graphql.ts +++ b/src/lib/graphql/generated/graphql.ts @@ -3599,7 +3599,7 @@ export type GetCategoryMangasQueryVariables = Exact<{ }>; -export type GetCategoryMangasQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, 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, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, 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, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } } }; +export type GetCategoryMangasQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, 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, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: 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, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } } }; export type ChapterMetaFieldsFragment = { __typename?: 'ChapterMetaType', chapterId: number, key: string, value: string }; @@ -3916,13 +3916,13 @@ export type MangaChapterStatFieldsFragment = { __typename?: 'MangaType', id: num export type MangaChapterNodeFieldsFragment = { __typename?: 'MangaType', firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }; -export type MangaReaderFieldsFragment = { __typename?: 'MangaType', genre: Array, 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, lang: string } | null, 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, 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, lang: string, iconUrl: 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, 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, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, 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, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }; +export type MangaLibraryFieldsFragment = { __typename?: 'MangaType', genre: Array, 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, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: 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, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }; export type MangaMigrationFieldsFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, 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, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }; -export type MangaScreenFieldsFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } }; +export type MangaScreenFieldsFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } }; export type MangaLibraryDuplicateScreenFieldsFragment = { __typename?: 'MangaType', 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, chapters: { __typename?: 'ChapterNodeList', totalCount: number } }; @@ -3931,7 +3931,7 @@ export type GetMangaFetchMutationVariables = Exact<{ }>; -export type GetMangaFetchMutation = { __typename?: 'Mutation', fetchManga?: { __typename?: 'FetchMangaPayload', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } } } | null }; +export type GetMangaFetchMutation = { __typename?: 'Mutation', fetchManga?: { __typename?: 'FetchMangaPayload', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } } } | null }; export type GetMangaToMigrateToFetchMutationVariables = Exact<{ id: Scalars['Int']['input']; @@ -3994,14 +3994,14 @@ export type GetMangaScreenQueryVariables = Exact<{ }>; -export type GetMangaScreenQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } } }; +export type GetMangaScreenQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } } }; export type GetMangaReaderQueryVariables = Exact<{ id: Scalars['Int']['input']; }>; -export type GetMangaReaderQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', genre: Array, 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, lang: string } | null, 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, 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, lang: string, iconUrl: 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<{ id: Scalars['Int']['input']; @@ -4054,7 +4054,7 @@ export type GetMangasLibraryQueryVariables = Exact<{ }>; -export type GetMangasLibraryQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, 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, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, 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, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; +export type GetMangasLibraryQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, 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, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: 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, chapterNumber: number, name: string, scanlator?: string | null } | 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, highestNumberedChapter?: { __typename?: 'ChapterType', id: number, chapterNumber: number } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; export type GetMangasDuplicatesQueryVariables = Exact<{ after?: InputMaybe; @@ -4185,13 +4185,11 @@ export type GetServerSettingsQuery = { __typename?: 'Query', settings: { __typen export type SourceMetaFieldsFragment = { __typename?: 'SourceMetaType', sourceId: string, key: string, value: string }; -export type SourceBaseFieldsFragment = { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string }; - -export type SourceMigratableFieldsFragment = { __typename?: 'SourceType', lang: string, iconUrl: string, id: string, name: string, displayName: string }; +export type SourceBaseFieldsFragment = { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string }; export type SourceListFieldsFragment = { __typename?: 'SourceType', lang: string, iconUrl: string, isNsfw: boolean, isConfigurable: boolean, supportsLatest: boolean, id: string, name: string, displayName: string, meta: Array<{ __typename?: 'SourceMetaType', sourceId: string, key: string, value: string }>, extension: { __typename?: 'ExtensionType', pkgName: string, repo?: string | null } }; -export type SourceBrowseFieldsFragment = { __typename?: 'SourceType', baseUrl?: string | null, isConfigurable: boolean, supportsLatest: boolean, id: string, name: string, displayName: string, lang: string, meta: Array<{ __typename?: 'SourceMetaType', sourceId: string, key: string, value: string }>, filters: Array< +export type SourceBrowseFieldsFragment = { __typename?: 'SourceType', baseUrl?: string | null, isConfigurable: boolean, supportsLatest: boolean, id: string, name: string, displayName: string, lang: string, iconUrl: string, meta: Array<{ __typename?: 'SourceMetaType', sourceId: string, key: string, value: string }>, filters: Array< | { __typename?: 'CheckBoxFilter', name: string, type: 'CheckBoxFilter', CheckBoxFilterDefault: boolean } | { __typename?: 'GroupFilter', name: string, type: 'GroupFilter', filters: Array< | { __typename?: 'CheckBoxFilter', name: string, type: 'CheckBoxFilter', CheckBoxFilterDefault: boolean } @@ -4211,7 +4209,7 @@ export type SourceBrowseFieldsFragment = { __typename?: 'SourceType', baseUrl?: | { __typename?: 'TriStateFilter', name: string, type: 'TriStateFilter', TriStateFilterDefault: TriState } > }; -export type SourceSettingFieldsFragment = { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, preferences: Array< +export type SourceSettingFieldsFragment = { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string, preferences: Array< | { __typename?: 'CheckBoxPreference', summary?: string | null, key?: string | null, type: 'CheckBoxPreference', CheckBoxCheckBoxCurrentValue?: boolean | null, CheckBoxDefault: boolean, CheckBoxTitle?: string | null } | { __typename?: 'EditTextPreference', text?: string | null, summary?: string | null, key?: string | null, dialogTitle?: string | null, dialogMessage?: string | null, type: 'EditTextPreference', EditTextPreferenceCurrentValue?: string | null, EditTextPreferenceDefault?: string | null, EditTextPreferenceTitle?: string | null } | { __typename?: 'ListPreference', summary?: string | null, key?: string | null, entryValues: Array, entries: Array, type: 'ListPreference', ListPreferenceCurrentValue?: string | null, ListPreferenceDefault?: string | null, ListPreferenceTitle?: string | null } @@ -4238,7 +4236,7 @@ export type UpdateSourcePreferencesMutationVariables = Exact<{ }>; -export type UpdateSourcePreferencesMutation = { __typename?: 'Mutation', updateSourcePreference?: { __typename?: 'UpdateSourcePreferencePayload', source: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, preferences: Array< +export type UpdateSourcePreferencesMutation = { __typename?: 'Mutation', updateSourcePreference?: { __typename?: 'UpdateSourcePreferencePayload', source: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string, preferences: Array< | { __typename?: 'CheckBoxPreference', summary?: string | null, key?: string | null, type: 'CheckBoxPreference', CheckBoxCheckBoxCurrentValue?: boolean | null, CheckBoxDefault: boolean, CheckBoxTitle?: string | null } | { __typename?: 'EditTextPreference', text?: string | null, summary?: string | null, key?: string | null, dialogTitle?: string | null, dialogMessage?: string | null, type: 'EditTextPreference', EditTextPreferenceCurrentValue?: string | null, EditTextPreferenceDefault?: string | null, EditTextPreferenceTitle?: string | null } | { __typename?: 'ListPreference', summary?: string | null, key?: string | null, entryValues: Array, entries: Array, type: 'ListPreference', ListPreferenceCurrentValue?: string | null, ListPreferenceDefault?: string | null, ListPreferenceTitle?: string | null } @@ -4265,7 +4263,7 @@ export type GetSourceBrowseQueryVariables = Exact<{ }>; -export type GetSourceBrowseQuery = { __typename?: 'Query', source: { __typename?: 'SourceType', baseUrl?: string | null, isConfigurable: boolean, supportsLatest: boolean, id: string, name: string, displayName: string, lang: string, meta: Array<{ __typename?: 'SourceMetaType', sourceId: string, key: string, value: string }>, filters: Array< +export type GetSourceBrowseQuery = { __typename?: 'Query', source: { __typename?: 'SourceType', baseUrl?: string | null, isConfigurable: boolean, supportsLatest: boolean, id: string, name: string, displayName: string, lang: string, iconUrl: string, meta: Array<{ __typename?: 'SourceMetaType', sourceId: string, key: string, value: string }>, filters: Array< | { __typename?: 'CheckBoxFilter', name: string, type: 'CheckBoxFilter', CheckBoxFilterDefault: boolean } | { __typename?: 'GroupFilter', name: string, type: 'GroupFilter', filters: Array< | { __typename?: 'CheckBoxFilter', name: string, type: 'CheckBoxFilter', CheckBoxFilterDefault: boolean } @@ -4290,7 +4288,7 @@ export type GetSourceSettingsQueryVariables = Exact<{ }>; -export type GetSourceSettingsQuery = { __typename?: 'Query', source: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, preferences: Array< +export type GetSourceSettingsQuery = { __typename?: 'Query', source: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string, preferences: Array< | { __typename?: 'CheckBoxPreference', summary?: string | null, key?: string | null, type: 'CheckBoxPreference', CheckBoxCheckBoxCurrentValue?: boolean | null, CheckBoxDefault: boolean, CheckBoxTitle?: string | null } | { __typename?: 'EditTextPreference', text?: string | null, summary?: string | null, key?: string | null, dialogTitle?: string | null, dialogMessage?: string | null, type: 'EditTextPreference', EditTextPreferenceCurrentValue?: string | null, EditTextPreferenceDefault?: string | null, EditTextPreferenceTitle?: string | null } | { __typename?: 'ListPreference', summary?: string | null, key?: string | null, entryValues: Array, entries: Array, type: 'ListPreference', ListPreferenceCurrentValue?: string | null, ListPreferenceDefault?: string | null, ListPreferenceTitle?: string | null } @@ -4303,7 +4301,7 @@ export type GetSourceMigratableQueryVariables = Exact<{ }>; -export type GetSourceMigratableQuery = { __typename?: 'Query', source: { __typename?: 'SourceType', lang: string, iconUrl: string, id: string, name: string, displayName: string } }; +export type GetSourceMigratableQuery = { __typename?: 'Query', source: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string } }; export type GetSourcesListQueryVariables = Exact<{ [key: string]: never; }>; @@ -4313,7 +4311,7 @@ export type GetSourcesListQuery = { __typename?: 'Query', sources: { __typename? export type GetMigratableSourcesQueryVariables = Exact<{ [key: string]: never; }>; -export type GetMigratableSourcesQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', nodes: Array<{ __typename?: 'MangaType', sourceId: string, source?: { __typename?: 'SourceType', lang: string, iconUrl: string, id: string, name: string, displayName: string } | null }> } }; +export type GetMigratableSourcesQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', nodes: Array<{ __typename?: 'MangaType', sourceId: string, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string, iconUrl: string } | null }> } }; export type TrackerBaseFieldsFragment = { __typename?: 'TrackerType', id: number, name: string, icon: string, isLoggedIn: boolean, isTokenExpired: boolean }; diff --git a/src/lib/graphql/manga/MangaFragments.ts b/src/lib/graphql/manga/MangaFragments.ts index 96df665d..583a45c8 100644 --- a/src/lib/graphql/manga/MangaFragments.ts +++ b/src/lib/graphql/manga/MangaFragments.ts @@ -112,6 +112,7 @@ export const MANGA_LIBRARY_FIELDS = gql` ${MANGA_CHAPTER_STAT_FIELDS} ${MANGA_CHAPTER_NODE_FIELDS} ${MANGA_META_FIELDS} + ${SOURCE_BASE_FIELDS} fragment MANGA_LIBRARY_FIELDS on MangaType { ...MANGA_BASE_FIELDS @@ -132,8 +133,7 @@ export const MANGA_LIBRARY_FIELDS = gql` } source { - id - displayName + ...SOURCE_BASE_FIELDS } trackRecords { diff --git a/src/lib/graphql/source/SourceFragments.ts b/src/lib/graphql/source/SourceFragments.ts index 80925d5e..4d3801c9 100644 --- a/src/lib/graphql/source/SourceFragments.ts +++ b/src/lib/graphql/source/SourceFragments.ts @@ -22,16 +22,6 @@ export const SOURCE_BASE_FIELDS = gql` name displayName lang - } -`; - -export const SOURCE_MIGRATABLE_FIELDS = gql` - ${SOURCE_BASE_FIELDS} - - fragment SOURCE_MIGRATABLE_FIELDS on SourceType { - ...SOURCE_BASE_FIELDS - - lang iconUrl } `; diff --git a/src/lib/graphql/source/SourceQuery.ts b/src/lib/graphql/source/SourceQuery.ts index 04456948..e00c978c 100644 --- a/src/lib/graphql/source/SourceQuery.ts +++ b/src/lib/graphql/source/SourceQuery.ts @@ -8,9 +8,9 @@ import gql from 'graphql-tag'; import { + SOURCE_BASE_FIELDS, SOURCE_BROWSE_FIELDS, SOURCE_LIST_FIELDS, - SOURCE_MIGRATABLE_FIELDS, SOURCE_SETTING_FIELDS, } from '@/lib/graphql/source/SourceFragments.ts'; @@ -35,11 +35,11 @@ export const GET_SOURCE_SETTINGS = gql` `; export const GET_SOURCE_MIGRATABLE = gql` - ${SOURCE_MIGRATABLE_FIELDS} + ${SOURCE_BASE_FIELDS} query GET_SOURCE_MIGRATABLE($id: LongString!) { source(id: $id) { - ...SOURCE_MIGRATABLE_FIELDS + ...SOURCE_BASE_FIELDS } } `; @@ -57,14 +57,14 @@ export const GET_SOURCES_LIST = gql` `; export const GET_MIGRATABLE_SOURCES = gql` - ${SOURCE_MIGRATABLE_FIELDS} + ${SOURCE_BASE_FIELDS} query GET_MIGRATABLE_SOURCES { mangas(condition: { inLibrary: true }) { nodes { sourceId source { - ...SOURCE_MIGRATABLE_FIELDS + ...SOURCE_BASE_FIELDS } } }