From 3eb34312ad8ebac10aca4e2b96fee02e281d9ddc Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:54:37 +0200 Subject: [PATCH] Resume migration on initial load --- src/App.tsx | 12 +++ src/features/migration/MigrationManager.ts | 93 +++++++++++-------- .../migration/screens/MigrationExecute.tsx | 2 +- 3 files changed, 68 insertions(+), 39 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index eb2241f5..39c4b830 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -33,6 +33,7 @@ import { ReactRouter } from '@/lib/react-router/ReactRouter.ts'; import { AuthManager } from '@/features/authentication/AuthManager.ts'; import { ImageProcessingType } from '@/features/settings/Settings.types.ts'; import { MigrationFABIndicator } from '@/features/migration/components/MigrationFABIndicator.tsx'; +import { MigrationManager } from '@/features/migration/MigrationManager.ts'; const { Browse } = loadable(() => import('@/features/browse/screens/Browse.tsx'), lazyLoadFallback); const { DownloadQueue } = loadable(() => import('@/features/downloads/screens/DownloadQueue.tsx'), lazyLoadFallback); @@ -156,6 +157,16 @@ const ReactRouterSetter = () => { return null; }; +const ResumeMigration = () => { + useEffect(() => { + if (!MigrationManager.isActive()) { + MigrationManager.resume().catch(defaultPromiseErrorHandler('ResumeMigration')); + } + }, []); + + return null; +}; + const PrivateRoutes = () => { const isAuthenticated = AuthManager.useIsAuthenticated(); @@ -342,6 +353,7 @@ export const App: React.FC = () => ( + diff --git a/src/features/migration/MigrationManager.ts b/src/features/migration/MigrationManager.ts index 3e17cf1f..1fe20229 100644 --- a/src/features/migration/MigrationManager.ts +++ b/src/features/migration/MigrationManager.ts @@ -347,8 +347,24 @@ export class MigrationManager { draft.groupExpandState = MIGRATE_SEARCH_ENTRY_GROUP_EXPAND_DEFAULT_STATE; }); + await MigrationManager.search(Object.values(state.entries)); + } + + private static async search(entries: TMigrationEntry[]): Promise { + const { signal } = MigrationManager.abortAndCreateAbortController('search'); + + const searchPromises = entries.map((entry) => + MigrationManager.mangaProcessQueue(async () => { + if (signal.aborted) { + return; + } + + await MigrationManager.searchForManga(entry.mangaId, entry.mangaTitle, signal); + }), + ); + try { - await MigrationManager.search(Object.values(state.entries)); + await Promise.allSettled(searchPromises); } finally { const { searchProgress } = MigrationManager.getState(); @@ -368,28 +384,12 @@ export class MigrationManager { } } - private static async search(entries: TMigrationEntry[]): Promise { - const { signal } = MigrationManager.abortAndCreateAbortController('search'); - - const searchPromises = entries.map((entry) => - MigrationManager.mangaProcessQueue(async () => { - if (signal.aborted) { - return; - } - - await MigrationManager.searchForManga(entry.mangaId, entry.mangaTitle, signal); - }), - ); - - await Promise.allSettled(searchPromises); - } - static getMigratableEntries(): MigratableEntry[] { const { entries } = MigrationManager.getState(); return Object.values(entries).filter( (entry): entry is MigratableEntry => - entry.status === MigrationEntryStatus.SEARCH_COMPLETE && + [MigrationEntryStatus.SEARCH_COMPLETE, MigrationEntryStatus.MIGRATING].includes(entry.status) && !entry.isExcluded && entry.selectedMatchMangaId != null && entry.selectedMatchSourceId != null, @@ -418,22 +418,7 @@ export class MigrationManager { draft.groupExpandState = MIGRATE_EXECUTE_ENTRY_GROUP_EXPAND_DEFAULT_STATE; }); - try { - await MigrationManager.migrate(migratableEntries, options); - } finally { - const { migrationProgress } = MigrationManager.getState(); - - if (migrationProgress.completed === migrationProgress.total) { - MigrationManager.updateState((draft) => { - draft.groupExpandState = { - ...MIGRATE_SEARCH_ENTRY_GROUP_EXPAND_DEFAULT_STATE, - [MigrationEntryStatus.MIGRATING]: false, - [MigrationEntryStatus.MIGRATION_FAILED]: !!migrationProgress.failed, - [MigrationEntryStatus.MIGRATION_COMPLETE]: !migrationProgress.failed, - }; - }); - } - } + await MigrationManager.migrate(migratableEntries, options); } private static async migrate( @@ -456,7 +441,23 @@ export class MigrationManager { } }), ); - await Promise.allSettled(migrationPromises); + + try { + await Promise.allSettled(migrationPromises); + } finally { + const { migrationProgress } = MigrationManager.getState(); + + if (migrationProgress.completed === migrationProgress.total) { + MigrationManager.updateState((draft) => { + draft.groupExpandState = { + ...MIGRATE_SEARCH_ENTRY_GROUP_EXPAND_DEFAULT_STATE, + [MigrationEntryStatus.MIGRATING]: false, + [MigrationEntryStatus.MIGRATION_FAILED]: !!migrationProgress.failed, + [MigrationEntryStatus.MIGRATION_COMPLETE]: !migrationProgress.failed, + }; + }); + } + } if (!signal.aborted) { MigrationManager.updateState((draft) => { @@ -493,15 +494,27 @@ export class MigrationManager { const migratableEntries = MigrationManager.getMigratableEntries(); + MigrationManager.updateState((draft) => { + migratableEntries.forEach((entry) => { + draft.entries[entry.mangaId].status = MigrationEntryStatus.SEARCH_COMPLETE; + }); + }); + await MigrationManager.migrate(migratableEntries, state.migrateOptions); return; } - const pendingEntries = Object.values(state.entries).filter( - (entry) => entry.status === MigrationEntryStatus.PENDING, + const pendingEntries = Object.values(state.entries).filter((entry) => + [MigrationEntryStatus.PENDING, MigrationEntryStatus.SEARCHING].includes(entry.status), ); + MigrationManager.updateState((draft) => { + pendingEntries.forEach((entry) => { + draft.entries[entry.mangaId].status = MigrationEntryStatus.PENDING; + }); + }); + await MigrationManager.search(pendingEntries); } @@ -574,7 +587,11 @@ export class MigrationManager { static isActive(): boolean { const { phase } = migrationStore.getState(); - return phase === MigrationPhase.SEARCHING || phase === MigrationPhase.MIGRATING; + + return ( + !!MigrationManager.abortController && + (phase === MigrationPhase.SEARCHING || phase === MigrationPhase.MIGRATING) + ); } static hasPausedMigration(): boolean { diff --git a/src/features/migration/screens/MigrationExecute.tsx b/src/features/migration/screens/MigrationExecute.tsx index 95befa64..0e6cf595 100644 --- a/src/features/migration/screens/MigrationExecute.tsx +++ b/src/features/migration/screens/MigrationExecute.tsx @@ -31,7 +31,7 @@ export const MigrationExecute = () => { const migratingEntries = useMemo( () => entryList.filter((entry) => - [MigrationEntryStatus.PENDING, MigrationEntryStatus.MIGRATING].includes(entry.status), + [MigrationEntryStatus.SEARCH_COMPLETE, MigrationEntryStatus.MIGRATING].includes(entry.status), ), [entryList], );