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],
);