diff --git a/src/features/manga/components/cards/MangaCard.tsx b/src/features/manga/components/cards/MangaCard.tsx index df8f116f..a21291c2 100644 --- a/src/features/manga/components/cards/MangaCard.tsx +++ b/src/features/manga/components/cards/MangaCard.tsx @@ -126,7 +126,7 @@ export const MangaCard = memo((props: MangaCardProps) => { try { try { - await MangaMigration.migrate(migrationSourceMangaId, id, options); + await MangaMigration.migrateByIdWithFetch(migrationSourceMangaId, id, options); } catch (e) { makeToast( t(MANGA_ACTION_TO_TRANSLATION['migrate'].error), diff --git a/src/features/migration/MangaMigration.ts b/src/features/migration/MangaMigration.ts index 4a50fa88..309379c8 100644 --- a/src/features/migration/MangaMigration.ts +++ b/src/features/migration/MangaMigration.ts @@ -14,13 +14,28 @@ import type { } from '@/lib/graphql/generated/graphql.ts'; import type { MangaIdInfo } from '@/features/manga/Manga.types.ts'; import { Chapters } from '@/features/chapter/services/Chapters.ts'; -import { getMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; import { ALL_APP_METADATA_KEY_PREFIXES } from '@/features/metadata/Metadata.constants.ts'; import type { MigrateMode, MigrateOptions } from '@/features/migration/Migration.types.ts'; +import type { + ChapterBookmarkInfo, + ChapterDownloadInfo, + ChapterIdInfo, + ChapterNumberInfo, + ChapterReadInfo, +} from '@/features/chapter/Chapter.types.ts'; +import type { GqlMetaHolder } from '@/features/metadata/Metadata.types.ts'; +import { getMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; type MangaToMigrate = NonNullable; type MangaToMigrateTo = NonNullable['manga']; +export type MigrationChapter = ChapterIdInfo & + ChapterReadInfo & + ChapterBookmarkInfo & + ChapterNumberInfo & + ChapterDownloadInfo & + GqlMetaHolder; + type MigrateAction = { copy: () => Promise[]; cleanup: () => Promise[] }; type MigrateActionCreator = () => MigrateAction; @@ -29,8 +44,9 @@ const performMigrationAction = async (migrateAction: keyof MigrateAction, ...act export class MangaMigration { static async migrate( - mangaId: MangaIdInfo['id'], - mangaIdToMigrateTo: number, + mangaToMigrate: MangaToMigrate | null | undefined, + mangaToMigrateTo: MangaToMigrateTo | null | undefined, + chaptersToMigrateTo: MigrationChapter[] | null | undefined, { mode, migrateChapters, @@ -39,37 +55,16 @@ export class MangaMigration { deleteChapters, migrateMetadata, }: Omit, + removeMangaFromCategories: boolean, ): Promise { - const [{ data: mangaToMigrateData }, { data: mangaToMigrateToData }, { removeMangaFromCategories }] = - await Promise.all([ - requestManager.getMangaToMigrate(mangaId, { - migrateChapters, - migrateCategories, - migrateTracking, - deleteChapters, - migrateMetadata, - }).response, - requestManager.getMangaToMigrateToFetch(mangaIdToMigrateTo, { - migrateChapters, - migrateCategories, - migrateTracking, - apolloOptions: { errorPolicy: 'all' }, - }).response, - getMetadataServerSettings(), - ]); - - if (!mangaToMigrateData?.manga || !mangaToMigrateToData?.fetchManga?.manga) { + if (!mangaToMigrate || !mangaToMigrateTo) { throw new Error('MangaMigration::migrate: missing manga data'); } - if (migrateChapters && !mangaToMigrateData.manga.chapters) { + if (migrateChapters && !mangaToMigrate.chapters) { throw new Error('MangaMigration::migrate: missing chapters data'); } - if (!mangaToMigrateToData.fetchChapters?.chapters) { - mangaToMigrateToData.fetchChapters = { chapters: [] }; - } - const performMigrationActions = async (...actionCreators: [boolean | undefined, MigrateActionCreator][]) => { const migrationActions: TupleUnion = ['copy', 'cleanup']; @@ -92,28 +87,20 @@ export class MangaMigration { () => MangaMigration.migrateChapters( mode, - mangaToMigrateData.manga, - mangaToMigrateToData, + mangaToMigrate.chapters?.nodes, + chaptersToMigrateTo, !!deleteChapters, !!migrateMetadata, ), ], - [ - migrateTracking, - () => - MangaMigration.migrateTracking( - mode, - mangaToMigrateData.manga, - mangaToMigrateToData.fetchManga!.manga, - ), - ], + [migrateTracking, () => MangaMigration.migrateTracking(mode, mangaToMigrate, mangaToMigrateTo)], [ true, () => MangaMigration.migrateManga( mode, - mangaToMigrateData.manga, - mangaToMigrateToData.fetchManga!.manga, + mangaToMigrate, + mangaToMigrateTo, !!migrateCategories, removeMangaFromCategories, !!migrateMetadata, @@ -122,20 +109,86 @@ export class MangaMigration { ); } + static async migrateByIdWithQuery( + mangaId: MangaIdInfo['id'], + mangaIdToMigrateTo: number, + options: Omit, + ): Promise { + const { migrateChapters, migrateCategories, migrateTracking, deleteChapters, migrateMetadata } = options; + + const [{ data: mangaToMigrateData }, { data: mangaToMigrateToData }, { removeMangaFromCategories }] = + await Promise.all([ + requestManager.getMangaToMigrate(mangaId, { + migrateChapters, + migrateCategories, + migrateTracking, + deleteChapters, + migrateMetadata, + }).response, + requestManager.getMangaToMigrate(mangaIdToMigrateTo, { + migrateChapters, + migrateCategories, + migrateTracking, + deleteChapters, + migrateMetadata, + }).response, + getMetadataServerSettings(), + ]); + + await MangaMigration.migrate( + mangaToMigrateData?.manga, + mangaToMigrateToData?.manga, + mangaToMigrateToData?.manga?.chapters?.nodes, + options, + removeMangaFromCategories, + ); + } + + static async migrateByIdWithFetch( + mangaId: MangaIdInfo['id'], + mangaIdToMigrateTo: number, + options: Omit, + ): Promise { + const { migrateChapters, migrateCategories, migrateTracking, deleteChapters, migrateMetadata } = options; + + const [{ data: mangaToMigrateData }, { data: mangaToMigrateToData }, { removeMangaFromCategories }] = + await Promise.all([ + requestManager.getMangaToMigrate(mangaId, { + migrateChapters, + migrateCategories, + migrateTracking, + deleteChapters, + migrateMetadata, + }).response, + requestManager.getMangaToMigrateToFetch(mangaIdToMigrateTo, { + migrateChapters, + migrateCategories, + migrateTracking, + apolloOptions: { errorPolicy: 'all' }, + }).response, + getMetadataServerSettings(), + ]); + + await MangaMigration.migrate( + mangaToMigrateData?.manga, + mangaToMigrateToData?.fetchManga?.manga, + mangaToMigrateToData?.fetchChapters?.chapters, + options, + removeMangaFromCategories, + ); + } + private static migrateChapters( mode: MigrateMode, - mangaToMigrate: GetMangaToMigrateQuery['manga'], - mangaToMigrateToInfo: GetMangaToMigrateToFetchMutation, + chaptersToMigrate: MigrationChapter[] | null | undefined, + chaptersToMigrateTo: MigrationChapter[] | null | undefined, deleteChapters: boolean, migrateMetadata: boolean, ): MigrateAction { - if (!mangaToMigrate.chapters || !mangaToMigrateToInfo.fetchChapters?.chapters) { + if (!chaptersToMigrate || !chaptersToMigrateTo) { throw new Error('Chapters are missing'); } - const chaptersToMigrate = mangaToMigrate.chapters.nodes; - - const chaptersToMigrateTo = mangaToMigrateToInfo.fetchChapters?.chapters; const migratableChapters = Chapters.getMatchingChapterNumberChapters(chaptersToMigrate, chaptersToMigrateTo); const highestReadChapterNumber = chaptersToMigrate.reduce((chapterNumber, chapterToMigrate) => { @@ -197,7 +250,7 @@ export class MangaMigration { ? [ deleteChapters ? requestManager.deleteDownloadedChapters( - Chapters.getIds(Chapters.getDownloaded(mangaToMigrate.chapters?.nodes ?? [])), + Chapters.getIds(Chapters.getDownloaded(chaptersToMigrate)), ).response : Promise.resolve(), ] diff --git a/src/features/migration/MigrationManager.ts b/src/features/migration/MigrationManager.ts index 9e00eb1f..cbeb3f84 100644 --- a/src/features/migration/MigrationManager.ts +++ b/src/features/migration/MigrationManager.ts @@ -963,7 +963,8 @@ export class MigrationManager { } assertIsDefined(entry.selectedMatchMangaId); - await MangaMigration.migrate(mangaId, entry.selectedMatchMangaId, options); + + await MangaMigration.migrateByIdWithQuery(mangaId, entry.selectedMatchMangaId, options); }); });