diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrationListScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrationListScreen.kt
new file mode 100644
index 000000000..fa6b08a6c
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/browse/MigrationListScreen.kt
@@ -0,0 +1,144 @@
+package eu.kanade.presentation.browse
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ArrowForward
+import androidx.compose.material.icons.outlined.ContentCopy
+import androidx.compose.material.icons.outlined.CopyAll
+import androidx.compose.material.icons.outlined.Done
+import androidx.compose.material.icons.outlined.DoneAll
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.produceState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import eu.kanade.domain.manga.model.Manga
+import eu.kanade.presentation.browse.components.MigrationActionIcon
+import eu.kanade.presentation.browse.components.MigrationItem
+import eu.kanade.presentation.browse.components.MigrationItemResult
+import eu.kanade.presentation.components.AppBar
+import eu.kanade.presentation.components.Scaffold
+import eu.kanade.presentation.components.ScrollbarLazyColumn
+import eu.kanade.presentation.util.plus
+import eu.kanade.presentation.util.topSmallPaddingValues
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga
+import eu.kanade.tachiyomi.util.lang.withIOContext
+
+@Composable
+fun MigrationListScreen(
+    items: List<MigratingManga>,
+    migrationDone: Boolean,
+    unfinishedCount: Int,
+    getManga: suspend (MigratingManga.SearchResult.Result) -> Manga?,
+    getChapterInfo: suspend (MigratingManga.SearchResult.Result) -> MigratingManga.ChapterInfo,
+    getSourceName: (Manga) -> String,
+    onMigrationItemClick: (Manga) -> Unit,
+    openMigrationDialog: (Boolean) -> Unit,
+    skipManga: (Long) -> Unit,
+    searchManually: (MigratingManga) -> Unit,
+    migrateNow: (Long) -> Unit,
+    copyNow: (Long) -> Unit,
+) {
+    Scaffold(
+        topBar = { scrollBehavior ->
+            val titleString = stringResource(R.string.migration)
+            val title by produceState(initialValue = titleString, items, unfinishedCount, titleString) {
+                withIOContext {
+                    value = "$titleString ($unfinishedCount/${items.size})"
+                }
+            }
+            AppBar(
+                title = title,
+                actions = {
+                    IconButton(
+                        onClick = { openMigrationDialog(true) },
+                        enabled = migrationDone,
+                    ) {
+                        Icon(
+                            imageVector = if (items.size == 1) Icons.Outlined.ContentCopy else Icons.Outlined.CopyAll,
+                            contentDescription = stringResource(R.string.copy),
+                        )
+                    }
+                    IconButton(
+                        onClick = { openMigrationDialog(false) },
+                        enabled = migrationDone,
+                    ) {
+                        Icon(
+                            imageVector = if (items.size == 1) Icons.Outlined.Done else Icons.Outlined.DoneAll,
+                            contentDescription = stringResource(R.string.migrate),
+                        )
+                    }
+                },
+                scrollBehavior = scrollBehavior,
+            )
+        },
+    ) { contentPadding ->
+        ScrollbarLazyColumn(
+            contentPadding = contentPadding + topSmallPaddingValues,
+        ) {
+            items(items, key = { it.manga.id }) { migrationItem ->
+                Row(
+                    Modifier
+                        .fillMaxWidth()
+                        .animateItemPlacement()
+                        .padding(horizontal = 16.dp),
+                    horizontalArrangement = Arrangement.SpaceBetween,
+                    verticalAlignment = Alignment.CenterVertically,
+                ) {
+                    val result by migrationItem.searchResult.collectAsState()
+                    MigrationItem(
+                        modifier = Modifier
+                            .padding(top = 8.dp)
+                            .weight(1f),
+                        manga = migrationItem.manga,
+                        sourcesString = migrationItem.sourcesString,
+                        chapterInfo = migrationItem.chapterInfo,
+                        onClick = { onMigrationItemClick(migrationItem.manga) },
+                    )
+
+                    Icon(
+                        Icons.Outlined.ArrowForward,
+                        contentDescription = stringResource(R.string.migrating_to),
+                        modifier = Modifier.weight(0.2f),
+                    )
+
+                    MigrationItemResult(
+                        modifier = Modifier
+                            .padding(top = 8.dp)
+                            .weight(1f),
+                        migrationItem = migrationItem,
+                        result = result,
+                        getManga = getManga,
+                        getChapterInfo = getChapterInfo,
+                        getSourceName = getSourceName,
+                        onMigrationItemClick = onMigrationItemClick,
+                    )
+
+                    MigrationActionIcon(
+                        modifier = Modifier
+                            .weight(0.2f),
+                        result = result,
+                        skipManga = { skipManga(migrationItem.manga.id) },
+                        searchManually = { searchManually(migrationItem) },
+                        migrateNow = {
+                            migrateNow(migrationItem.manga.id)
+                        },
+                        copyNow = {
+                            copyNow(migrationItem.manga.id)
+                        },
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/MigrationActionIcon.kt b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationActionIcon.kt
new file mode 100644
index 000000000..91987a825
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationActionIcon.kt
@@ -0,0 +1,88 @@
+package eu.kanade.presentation.browse.components
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Close
+import androidx.compose.material.icons.outlined.MoreVert
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.dp
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga
+import me.saket.cascade.CascadeDropdownMenu
+
+@Composable
+fun MigrationActionIcon(
+    modifier: Modifier,
+    result: MigratingManga.SearchResult,
+    skipManga: () -> Unit,
+    searchManually: () -> Unit,
+    migrateNow: () -> Unit,
+    copyNow: () -> Unit,
+) {
+    var moreExpanded by remember { mutableStateOf(false) }
+    val closeMenu = { moreExpanded = false }
+
+    Box(modifier) {
+        if (result is MigratingManga.SearchResult.Searching) {
+            IconButton(onClick = skipManga) {
+                Icon(
+                    imageVector = Icons.Outlined.Close,
+                    contentDescription = stringResource(R.string.action_stop),
+                )
+            }
+        } else if (result is MigratingManga.SearchResult.Result || result is MigratingManga.SearchResult.NotFound) {
+            IconButton(onClick = { moreExpanded = !moreExpanded }) {
+                Icon(
+                    imageVector = Icons.Outlined.MoreVert,
+                    contentDescription = stringResource(R.string.abc_action_menu_overflow_description),
+                )
+            }
+            CascadeDropdownMenu(
+                expanded = moreExpanded,
+                onDismissRequest = closeMenu,
+                offset = DpOffset(8.dp, (-56).dp),
+            ) {
+                androidx.compose.material3.DropdownMenuItem(
+                    text = { Text(stringResource(R.string.action_search_manually)) },
+                    onClick = {
+                        searchManually()
+                        closeMenu()
+                    },
+                )
+                androidx.compose.material3.DropdownMenuItem(
+                    text = { Text(stringResource(R.string.action_skip_manga)) },
+                    onClick = {
+                        skipManga()
+                        closeMenu()
+                    },
+                )
+                if (result is MigratingManga.SearchResult.Result) {
+                    androidx.compose.material3.DropdownMenuItem(
+                        text = { Text(stringResource(R.string.action_migrate_now)) },
+                        onClick = {
+                            migrateNow()
+                            closeMenu()
+                        },
+                    )
+                    androidx.compose.material3.DropdownMenuItem(
+                        text = { Text(stringResource(R.string.action_copy_now)) },
+                        onClick = {
+                            copyNow()
+                            closeMenu()
+                        },
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/MigrationExitDialog.kt b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationExitDialog.kt
new file mode 100644
index 000000000..4a1898953
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationExitDialog.kt
@@ -0,0 +1,31 @@
+package eu.kanade.presentation.browse.components
+
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import eu.kanade.tachiyomi.R
+
+@Composable
+fun MigrationExitDialog(
+    onDismissRequest: () -> Unit,
+    exitMigration: () -> Unit,
+) {
+    AlertDialog(
+        onDismissRequest = onDismissRequest,
+        confirmButton = {
+            TextButton(onClick = exitMigration) {
+                Text(text = stringResource(R.string.action_stop))
+            }
+        },
+        dismissButton = {
+            TextButton(onClick = onDismissRequest) {
+                Text(text = stringResource(android.R.string.cancel))
+            }
+        },
+        title = {
+            Text(text = stringResource(R.string.stop_migrating))
+        },
+    )
+}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItem.kt b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItem.kt
new file mode 100644
index 000000000..7398ea47a
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItem.kt
@@ -0,0 +1,118 @@
+package eu.kanade.presentation.browse.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.produceState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shadow
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import eu.kanade.domain.manga.model.Manga
+import eu.kanade.presentation.components.Badge
+import eu.kanade.presentation.components.BadgeGroup
+import eu.kanade.presentation.components.MangaCover
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga
+import eu.kanade.tachiyomi.util.lang.withIOContext
+
+@Composable
+fun MigrationItem(
+    modifier: Modifier,
+    manga: Manga,
+    sourcesString: String,
+    chapterInfo: MigratingManga.ChapterInfo,
+    onClick: () -> Unit,
+) {
+    Column(
+        modifier
+            .widthIn(max = 150.dp)
+            .fillMaxWidth()
+            .clip(MaterialTheme.shapes.small)
+            .clickable(onClick = onClick)
+            .padding(4.dp),
+    ) {
+        val context = LocalContext.current
+        Box(
+            Modifier.fillMaxWidth()
+                .aspectRatio(MangaCover.Book.ratio),
+        ) {
+            MangaCover.Book(
+                modifier = Modifier
+                    .fillMaxWidth(),
+                data = manga,
+            )
+            Box(
+                modifier = Modifier
+                    .clip(RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp))
+                    .background(
+                        Brush.verticalGradient(
+                            0f to Color.Transparent,
+                            1f to Color(0xAA000000),
+                        ),
+                    )
+                    .fillMaxHeight(0.33f)
+                    .fillMaxWidth()
+                    .align(Alignment.BottomCenter),
+            )
+            Text(
+                modifier = Modifier
+                    .padding(8.dp)
+                    .align(Alignment.BottomStart),
+                text = manga.title.ifBlank { stringResource(R.string.unknown) },
+                fontSize = 12.sp,
+                lineHeight = 18.sp,
+                maxLines = 2,
+                overflow = TextOverflow.Ellipsis,
+                style = MaterialTheme.typography.titleSmall.copy(
+                    color = Color.White,
+                    shadow = Shadow(
+                        color = Color.Black,
+                        blurRadius = 4f,
+                    ),
+                ),
+            )
+            BadgeGroup(modifier = Modifier.padding(4.dp)) {
+                Badge(text = "${chapterInfo.chapterCount}")
+            }
+        }
+        Text(
+            text = sourcesString,
+            modifier = Modifier.padding(top = 4.dp, bottom = 1.dp, start = 8.dp),
+            overflow = TextOverflow.Ellipsis,
+            maxLines = 1,
+            style = MaterialTheme.typography.titleSmall,
+        )
+
+        val formattedLatestChapter by produceState(initialValue = "") {
+            value = withIOContext {
+                chapterInfo.getFormattedLatestChapter(context)
+            }
+        }
+        Text(
+            text = formattedLatestChapter,
+            modifier = Modifier.padding(top = 1.dp, bottom = 4.dp, start = 8.dp),
+            overflow = TextOverflow.Ellipsis,
+            maxLines = 1,
+            style = MaterialTheme.typography.bodyMedium,
+        )
+    }
+}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItemResult.kt b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItemResult.kt
new file mode 100644
index 000000000..668d71a35
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItemResult.kt
@@ -0,0 +1,84 @@
+package eu.kanade.presentation.browse.components
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.produceState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import eu.kanade.domain.manga.model.Manga
+import eu.kanade.presentation.components.MangaCover
+import eu.kanade.presentation.util.rememberResourceBitmapPainter
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga
+import eu.kanade.tachiyomi.util.lang.withIOContext
+
+@Composable
+fun MigrationItemResult(
+    modifier: Modifier,
+    migrationItem: MigratingManga,
+    result: MigratingManga.SearchResult,
+    getManga: suspend (MigratingManga.SearchResult.Result) -> Manga?,
+    getChapterInfo: suspend (MigratingManga.SearchResult.Result) -> MigratingManga.ChapterInfo,
+    getSourceName: (Manga) -> String,
+    onMigrationItemClick: (Manga) -> Unit,
+) {
+    Box(modifier) {
+        when (result) {
+            MigratingManga.SearchResult.Searching -> Box(
+                modifier = Modifier
+                    .widthIn(max = 150.dp)
+                    .fillMaxWidth()
+                    .aspectRatio(MangaCover.Book.ratio),
+                contentAlignment = Alignment.Center,
+            ) {
+                CircularProgressIndicator()
+            }
+            MigratingManga.SearchResult.NotFound -> Image(
+                painter = rememberResourceBitmapPainter(id = R.drawable.cover_error),
+                contentDescription = null,
+                modifier = Modifier
+                    .matchParentSize()
+                    .clip(RoundedCornerShape(4.dp)),
+                contentScale = ContentScale.Crop,
+            )
+            is MigratingManga.SearchResult.Result -> {
+                val item by produceState<Triple<Manga, MigratingManga.ChapterInfo, String>?>(
+                    initialValue = null,
+                    migrationItem,
+                    result,
+                ) {
+                    value = withIOContext {
+                        val manga = getManga(result) ?: return@withIOContext null
+                        Triple(
+                            manga,
+                            getChapterInfo(result),
+                            getSourceName(manga),
+                        )
+                    }
+                }
+                if (item != null) {
+                    val (manga, chapterInfo, source) = item!!
+                    MigrationItem(
+                        modifier = Modifier,
+                        manga = manga,
+                        sourcesString = source,
+                        chapterInfo = chapterInfo,
+                        onClick = {
+                            onMigrationItemClick(manga)
+                        },
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/MigrationMangaDialog.kt b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationMangaDialog.kt
new file mode 100644
index 000000000..db1f4ca0d
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationMangaDialog.kt
@@ -0,0 +1,51 @@
+package eu.kanade.presentation.browse.components
+
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.pluralStringResource
+import androidx.compose.ui.res.stringResource
+import eu.kanade.tachiyomi.R
+
+@Composable
+fun MigrationMangaDialog(
+    onDismissRequest: () -> Unit,
+    copy: Boolean,
+    mangaSet: Int,
+    mangaSkipped: Int,
+    copyManga: () -> Unit,
+    migrateManga: () -> Unit,
+) {
+    AlertDialog(
+        onDismissRequest = onDismissRequest,
+        confirmButton = {
+            TextButton(
+                onClick = {
+                    if (copy) {
+                        copyManga()
+                    } else {
+                        migrateManga()
+                    }
+                },
+            ) {
+                Text(text = stringResource(if (copy) R.string.copy else R.string.migrate))
+            }
+        },
+        dismissButton = {
+            TextButton(onClick = onDismissRequest) {
+                Text(text = stringResource(android.R.string.cancel))
+            }
+        },
+        text = {
+            Text(
+                text = pluralStringResource(
+                    if (copy) R.plurals.copy_manga else R.plurals.migrate_manga,
+                    count = mangaSet,
+                    mangaSet,
+                    (if (mangaSkipped > 0) " " + stringResource(R.string.skipping_, mangaSkipped) else ""),
+                ),
+            )
+        },
+    )
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationMangaDialog.kt
deleted file mode 100644
index 9ade55106..000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationMangaDialog.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package eu.kanade.tachiyomi.ui.browse.migration
-
-import android.app.Dialog
-import android.os.Bundle
-import com.bluelinelabs.conductor.Controller
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.ui.base.controller.DialogController
-import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListController
-
-class MigrationMangaDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
-    where T : Controller {
-
-    var copy = false
-    var mangaSet = 0
-    var mangaSkipped = 0
-    constructor(target: T, copy: Boolean, mangaSet: Int, mangaSkipped: Int) : this() {
-        targetController = target
-        this.copy = copy
-        this.mangaSet = mangaSet
-        this.mangaSkipped = mangaSkipped
-    }
-
-    override fun onCreateDialog(savedViewState: Bundle?): Dialog {
-        val confirmRes = if (copy) R.plurals.copy_manga else R.plurals.migrate_manga
-        val confirmString = applicationContext?.resources?.getQuantityString(
-            confirmRes,
-            mangaSet,
-            mangaSet,
-            (if (mangaSkipped > 0) " " + applicationContext?.getString(R.string.skipping_, mangaSkipped) else ""),
-        ).orEmpty()
-        return MaterialAlertDialogBuilder(activity!!)
-            .setMessage(confirmString)
-            .setPositiveButton(if (copy) R.string.copy else R.string.migrate) { _, _ ->
-                if (copy) {
-                    (targetController as? MigrationListController)?.copyMangas()
-                } else {
-                    (targetController as? MigrationListController)?.migrateMangas()
-                }
-            }
-            .setNegativeButton(android.R.string.cancel, null)
-            .create()
-    }
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationBottomSheetDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationBottomSheetDialog.kt
index c63c6216e..643d799cf 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationBottomSheetDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationBottomSheetDialog.kt
@@ -1,6 +1,6 @@
 package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
 
-import android.app.Activity
+import android.content.Context
 import android.content.res.Resources
 import android.os.Bundle
 import android.view.LayoutInflater
@@ -21,13 +21,13 @@ import eu.kanade.tachiyomi.util.system.toast
 import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
 import uy.kohesive.injekt.injectLazy
 
-class MigrationBottomSheetDialog(private val activity: Activity, private val listener: StartMigrationListener) : BaseBottomSheetDialog(activity) {
+class MigrationBottomSheetDialog(private val baseContext: Context, private val listener: StartMigrationListener) : BaseBottomSheetDialog(baseContext) {
     private val preferences: UnsortedPreferences by injectLazy()
 
     lateinit var binding: MigrationBottomSheetBinding
 
     override fun createView(inflater: LayoutInflater): View {
-        binding = MigrationBottomSheetBinding.inflate(activity.layoutInflater)
+        binding = MigrationBottomSheetBinding.inflate(LayoutInflater.from(baseContext))
         return binding.root
     }
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceAdapter.kt
index 1e45b08aa..793b56cf5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceAdapter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/MigrationSourceAdapter.kt
@@ -1,16 +1,15 @@
 package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
 
-import android.os.Bundle
 import eu.davidea.flexibleadapter.FlexibleAdapter
 import eu.kanade.domain.source.service.SourcePreferences
 import eu.kanade.tachiyomi.source.SourceManager
 import uy.kohesive.injekt.injectLazy
 
 class MigrationSourceAdapter(
-    controllerPre: PreMigrationController,
+    listener: FlexibleAdapter.OnItemClickListener,
 ) : FlexibleAdapter<MigrationSourceItem>(
     null,
-    controllerPre,
+    listener,
     true,
 ) {
     val sourceManager: SourceManager by injectLazy()
@@ -18,32 +17,4 @@ class MigrationSourceAdapter(
     // SY _->
     val sourcePreferences: SourcePreferences by injectLazy()
     // SY <--
-
-    override fun onSaveInstanceState(outState: Bundle) {
-        super.onSaveInstanceState(outState)
-
-        outState.putParcelableArrayList(
-            SELECTED_SOURCES_KEY,
-            ArrayList(
-                currentItems.map {
-                    it.asParcelable()
-                },
-            ),
-        )
-    }
-
-    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
-        val selectedSources = savedInstanceState
-            .getParcelableArrayList<MigrationSourceItem.MigrationSource>(SELECTED_SOURCES_KEY)
-
-        if (selectedSources != null) {
-            updateDataSet(selectedSources.map { MigrationSourceItem.fromParcelable(sourceManager, it) })
-        }
-
-        super.onRestoreInstanceState(savedInstanceState)
-    }
-
-    companion object {
-        private const val SELECTED_SOURCES_KEY = "selected_sources"
-    }
 }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationController.kt
index 4773ade7b..25b7e26a3 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationController.kt
@@ -1,65 +1,16 @@
 package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
 
 import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.ArrowForward
-import androidx.compose.material.icons.outlined.Deselect
-import androidx.compose.material.icons.outlined.SelectAll
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBarDefaults
-import androidx.compose.material3.rememberTopAppBarState
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
-import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.Velocity
-import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.os.bundleOf
-import androidx.core.view.ViewCompat
-import androidx.core.view.updateLayoutParams
-import androidx.core.view.updatePadding
-import androidx.recyclerview.widget.LinearLayoutManager
+import cafe.adriel.voyager.navigator.Navigator
 import com.bluelinelabs.conductor.Router
-import eu.davidea.flexibleadapter.FlexibleAdapter
-import eu.kanade.domain.UnsortedPreferences
-import eu.kanade.domain.source.service.SourcePreferences
-import eu.kanade.presentation.components.AppBar
-import eu.kanade.presentation.components.ExtendedFloatingActionButton
-import eu.kanade.presentation.components.OverflowMenu
-import eu.kanade.presentation.components.Scaffold
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.databinding.PreMigrationListBinding
-import eu.kanade.tachiyomi.source.SourceManager
-import eu.kanade.tachiyomi.source.online.HttpSource
 import eu.kanade.tachiyomi.ui.base.controller.BasicFullComposeController
 import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
 import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListController
 import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationProcedureConfig
-import eu.kanade.tachiyomi.util.lang.launchIO
-import uy.kohesive.injekt.injectLazy
-import kotlin.math.roundToInt
 
-class PreMigrationController(bundle: Bundle? = null) :
-    BasicFullComposeController(bundle),
-    FlexibleAdapter.OnItemClickListener,
-    StartMigrationListener {
+class PreMigrationController(bundle: Bundle? = null) : BasicFullComposeController(bundle) {
 
     constructor(mangaIds: List<Long>) : this(
         bundleOf(
@@ -67,275 +18,11 @@ class PreMigrationController(bundle: Bundle? = null) :
         ),
     )
 
-    private val sourceManager: SourceManager by injectLazy()
-    private val prefs: UnsortedPreferences by injectLazy()
-    private val sourcePreferences: SourcePreferences by injectLazy()
-
-    private var adapter: MigrationSourceAdapter? = null
-
     private val config: LongArray = args.getLongArray(MANGA_IDS_EXTRA) ?: LongArray(0)
 
-    private lateinit var dialog: MigrationBottomSheetDialog
-
-    private lateinit var controllerBinding: PreMigrationListBinding
-
-    var items by mutableStateOf(emptyList<MigrationSourceItem>())
-
-    override fun onViewCreated(view: View) {
-        super.onViewCreated(view)
-
-        dialog = MigrationBottomSheetDialog(activity!!, this)
-
-        viewScope.launchIO {
-            items = getEnabledSources()
-        }
-    }
-
     @Composable
     override fun ComposeContent() {
-        val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
-        var fabExpanded by remember { mutableStateOf(true) }
-        val nestedScrollConnection = remember {
-            // All this lines just for fab state :/
-            object : NestedScrollConnection {
-                override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
-                    fabExpanded = available.y >= 0
-                    return scrollBehavior.nestedScrollConnection.onPreScroll(available, source)
-                }
-
-                override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
-                    return scrollBehavior.nestedScrollConnection.onPostScroll(consumed, available, source)
-                }
-
-                override suspend fun onPreFling(available: Velocity): Velocity {
-                    return scrollBehavior.nestedScrollConnection.onPreFling(available)
-                }
-
-                override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
-                    return scrollBehavior.nestedScrollConnection.onPostFling(consumed, available)
-                }
-            }
-        }
-        Scaffold(
-            topBar = {
-                AppBar(
-                    title = stringResource(R.string.select_sources),
-                    navigateUp = router::popCurrentController,
-                    scrollBehavior = scrollBehavior,
-                    actions = {
-                        IconButton(onClick = { massSelect(false) }) {
-                            Icon(
-                                imageVector = Icons.Outlined.Deselect,
-                                contentDescription = stringResource(R.string.select_none),
-                            )
-                        }
-                        IconButton(onClick = { massSelect(true) }) {
-                            Icon(
-                                imageVector = Icons.Outlined.SelectAll,
-                                contentDescription = stringResource(R.string.action_select_all),
-                            )
-                        }
-                        OverflowMenu { closeMenu ->
-                            DropdownMenuItem(
-                                text = { Text(stringResource(R.string.match_enabled_sources)) },
-                                onClick = {
-                                    matchSelection(true)
-                                    closeMenu()
-                                },
-                            )
-                            DropdownMenuItem(
-                                text = { Text(stringResource(R.string.match_pinned_sources)) },
-                                onClick = {
-                                    matchSelection(false)
-                                    closeMenu()
-                                },
-                            )
-                        }
-                    },
-                )
-            },
-            floatingActionButton = {
-                ExtendedFloatingActionButton(
-                    text = { Text(text = stringResource(R.string.action_migrate)) },
-                    icon = {
-                        Icon(
-                            imageVector = Icons.Outlined.ArrowForward,
-                            contentDescription = stringResource(R.string.action_migrate),
-                        )
-                    },
-                    onClick = {
-                        if (!dialog.isShowing) {
-                            dialog.show()
-                        }
-                    },
-                    expanded = fabExpanded,
-                    modifier = Modifier.navigationBarsPadding(),
-                )
-            },
-        ) { contentPadding ->
-            val density = LocalDensity.current
-            val layoutDirection = LocalLayoutDirection.current
-            val left = with(density) { contentPadding.calculateLeftPadding(layoutDirection).toPx().roundToInt() }
-            val top = with(density) { contentPadding.calculateTopPadding().toPx().roundToInt() }
-            val right = with(density) { contentPadding.calculateRightPadding(layoutDirection).toPx().roundToInt() }
-            val bottom = with(density) { contentPadding.calculateBottomPadding().toPx().roundToInt() }
-            Box(modifier = Modifier.nestedScroll(nestedScrollConnection)) {
-                AndroidView(
-                    factory = { context ->
-                        controllerBinding = PreMigrationListBinding.inflate(LayoutInflater.from(context))
-                        adapter = MigrationSourceAdapter(this@PreMigrationController)
-                        controllerBinding.recycler.adapter = adapter
-                        adapter?.isHandleDragEnabled = true
-                        adapter?.fastScroller = controllerBinding.fastScroller
-                        controllerBinding.recycler.layoutManager = LinearLayoutManager(context)
-
-                        ViewCompat.setNestedScrollingEnabled(controllerBinding.root, true)
-
-                        controllerBinding.root
-                    },
-                    update = {
-                        controllerBinding.recycler
-                            .updatePadding(
-                                left = left,
-                                top = top,
-                                right = right,
-                                bottom = bottom,
-                            )
-
-                        controllerBinding.fastScroller
-                            .updateLayoutParams<ViewGroup.MarginLayoutParams> {
-                                leftMargin = left
-                                topMargin = top
-                                rightMargin = right
-                                bottomMargin = bottom
-                            }
-
-                        adapter?.updateDataSet(items)
-                    },
-                )
-            }
-        }
-    }
-
-    override fun startMigration(extraParam: String?) {
-        val listOfSources = adapter?.currentItems
-            ?.filterIsInstance<MigrationSourceItem>()
-            ?.filter {
-                it.sourceEnabled
-            }
-            ?.joinToString("/") { it.source.id.toString() }
-            .orEmpty()
-
-        prefs.migrationSources().set(listOfSources)
-
-        router.replaceTopController(
-            MigrationListController(
-                MigrationProcedureConfig(
-                    config.toList(),
-                    extraSearchParams = extraParam,
-                ),
-            ).withFadeTransaction().tag(MigrationListController.TAG),
-        )
-    }
-
-    override fun onDestroyView(view: View) {
-        adapter = null
-        super.onDestroyView(view)
-    }
-
-    override fun onSaveInstanceState(outState: Bundle) {
-        super.onSaveInstanceState(outState)
-        adapter?.onSaveInstanceState(outState)
-    }
-
-    // TODO Still incorrect, why is this called before onViewCreated?
-    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
-        super.onRestoreInstanceState(savedInstanceState)
-        adapter?.onRestoreInstanceState(savedInstanceState)
-    }
-
-    override fun onItemClick(view: View, position: Int): Boolean {
-        val adapter = adapter ?: return false
-        adapter.getItem(position)?.let {
-            it.sourceEnabled = !it.sourceEnabled
-        }
-        adapter.notifyItemChanged(position)
-        return false
-    }
-
-    /**
-     * Returns a list of enabled sources ordered by language and name.
-     *
-     * @return list containing enabled sources.
-     */
-    private fun getEnabledSources(): List<MigrationSourceItem> {
-        val languages = sourcePreferences.enabledLanguages().get()
-        val sourcesSaved = prefs.migrationSources().get().split("/")
-            .mapNotNull { it.toLongOrNull() }
-        val disabledSources = sourcePreferences.disabledSources().get()
-            .mapNotNull { it.toLongOrNull() }
-        val sources = sourceManager.getVisibleCatalogueSources()
-            .asSequence()
-            .filterIsInstance<HttpSource>()
-            .filter { it.lang in languages }
-            .sortedBy { "(${it.lang}) ${it.name}" }
-            .map {
-                MigrationSourceItem(
-                    it,
-                    isEnabled(
-                        sourcesSaved,
-                        disabledSources,
-                        it.id,
-                    ),
-                )
-            }
-            .toList()
-
-        return sources
-            .filter { it.sourceEnabled }
-            .sortedBy { sourcesSaved.indexOf(it.source.id) }
-            .plus(
-                sources.filterNot { it.sourceEnabled },
-            )
-    }
-
-    fun isEnabled(
-        sourcesSaved: List<Long>,
-        disabledSources: List<Long>,
-        id: Long,
-    ): Boolean {
-        return if (sourcesSaved.isEmpty()) {
-            id !in disabledSources
-        } else {
-            id in sourcesSaved
-        }
-    }
-
-    private fun massSelect(selectAll: Boolean) {
-        val adapter = adapter ?: return
-        adapter.currentItems.forEach {
-            it.sourceEnabled = selectAll
-        }
-        adapter.notifyDataSetChanged()
-    }
-
-    private fun matchSelection(matchEnabled: Boolean) {
-        val adapter = adapter ?: return
-        val enabledSources = if (matchEnabled) {
-            sourcePreferences.disabledSources().get().mapNotNull { it.toLongOrNull() }
-        } else {
-            sourcePreferences.pinnedSources().get().mapNotNull { it.toLongOrNull() }
-        }
-        val items = adapter.currentItems.toList()
-        items.forEach {
-            it.sourceEnabled = if (matchEnabled) {
-                it.source.id !in enabledSources
-            } else {
-                it.source.id in enabledSources
-            }
-        }
-        val sortedItems = items.sortedBy { it.source.name }.sortedBy { !it.sourceEnabled }
-        adapter.updateDataSet(sortedItems)
+        Navigator(screen = PreMigrationScreen(config.toList()))
     }
 
     companion object {
@@ -349,7 +36,7 @@ class PreMigrationController(bundle: Bundle? = null) :
                     )
                 } else {
                     PreMigrationController(mangaIds)
-                }.withFadeTransaction().tag(if (skipPre) MigrationListController.TAG else null),
+                }.withFadeTransaction().tag(MigrationListController.TAG),
             )
         }
     }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationScreen.kt
new file mode 100644
index 000000000..369e0954b
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationScreen.kt
@@ -0,0 +1,217 @@
+package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ArrowForward
+import androidx.compose.material.icons.outlined.Deselect
+import androidx.compose.material.icons.outlined.SelectAll
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberTopAppBarState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.ViewCompat
+import androidx.core.view.updateLayoutParams
+import androidx.core.view.updatePadding
+import androidx.recyclerview.widget.LinearLayoutManager
+import cafe.adriel.voyager.core.model.rememberScreenModel
+import cafe.adriel.voyager.core.screen.Screen
+import cafe.adriel.voyager.navigator.LocalNavigator
+import cafe.adriel.voyager.navigator.Navigator
+import cafe.adriel.voyager.navigator.currentOrThrow
+import eu.kanade.presentation.components.AppBar
+import eu.kanade.presentation.components.ExtendedFloatingActionButton
+import eu.kanade.presentation.components.OverflowMenu
+import eu.kanade.presentation.components.Scaffold
+import eu.kanade.presentation.util.LocalRouter
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.databinding.PreMigrationListBinding
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationProcedureConfig
+import kotlin.math.roundToInt
+
+class PreMigrationScreen(val mangaIds: List<Long>) : Screen {
+
+    @Composable
+    override fun Content() {
+        val screenModel = rememberScreenModel { PreMigrationScreenModel() }
+        val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
+        val router = LocalRouter.currentOrThrow
+        val navigator = LocalNavigator.currentOrThrow
+        var fabExpanded by remember { mutableStateOf(true) }
+        val items by screenModel.state.collectAsState()
+        val context = LocalContext.current
+        DisposableEffect(screenModel) {
+            screenModel.dialog = MigrationBottomSheetDialog(context, screenModel.listener)
+            onDispose {}
+        }
+
+        LaunchedEffect(screenModel) {
+            screenModel.startMigration.collect { extraParam ->
+                navigator replace MigrationListScreen(MigrationProcedureConfig(mangaIds, extraParam))
+            }
+        }
+
+        val nestedScrollConnection = remember {
+            // All this lines just for fab state :/
+            object : NestedScrollConnection {
+                override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                    fabExpanded = available.y >= 0
+                    return scrollBehavior.nestedScrollConnection.onPreScroll(available, source)
+                }
+
+                override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
+                    return scrollBehavior.nestedScrollConnection.onPostScroll(consumed, available, source)
+                }
+
+                override suspend fun onPreFling(available: Velocity): Velocity {
+                    return scrollBehavior.nestedScrollConnection.onPreFling(available)
+                }
+
+                override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+                    return scrollBehavior.nestedScrollConnection.onPostFling(consumed, available)
+                }
+            }
+        }
+        Scaffold(
+            topBar = {
+                AppBar(
+                    title = stringResource(R.string.select_sources),
+                    navigateUp = {
+                        when {
+                            navigator.canPop -> navigator.pop()
+                            else -> router.popCurrentController()
+                        }
+                    },
+                    scrollBehavior = scrollBehavior,
+                    actions = {
+                        IconButton(onClick = { screenModel.massSelect(false) }) {
+                            Icon(
+                                imageVector = Icons.Outlined.Deselect,
+                                contentDescription = stringResource(R.string.select_none),
+                            )
+                        }
+                        IconButton(onClick = { screenModel.massSelect(true) }) {
+                            Icon(
+                                imageVector = Icons.Outlined.SelectAll,
+                                contentDescription = stringResource(R.string.action_select_all),
+                            )
+                        }
+                        OverflowMenu { closeMenu ->
+                            androidx.compose.material3.DropdownMenuItem(
+                                text = { Text(stringResource(R.string.match_enabled_sources)) },
+                                onClick = {
+                                    screenModel.matchSelection(true)
+                                    closeMenu()
+                                },
+                            )
+                            androidx.compose.material3.DropdownMenuItem(
+                                text = { Text(stringResource(R.string.match_pinned_sources)) },
+                                onClick = {
+                                    screenModel.matchSelection(false)
+                                    closeMenu()
+                                },
+                            )
+                        }
+                    },
+                )
+            },
+            floatingActionButton = {
+                ExtendedFloatingActionButton(
+                    text = { Text(text = stringResource(R.string.action_migrate)) },
+                    icon = {
+                        Icon(
+                            imageVector = Icons.Outlined.ArrowForward,
+                            contentDescription = stringResource(R.string.action_migrate),
+                        )
+                    },
+                    onClick = {
+                        if (!screenModel.dialog.isShowing) {
+                            screenModel.dialog.show()
+                        }
+                    },
+                    expanded = fabExpanded,
+                    modifier = Modifier.navigationBarsPadding(),
+                )
+            },
+        ) { contentPadding ->
+            val density = LocalDensity.current
+            val layoutDirection = LocalLayoutDirection.current
+            val left = with(density) { contentPadding.calculateLeftPadding(layoutDirection).toPx().roundToInt() }
+            val top = with(density) { contentPadding.calculateTopPadding().toPx().roundToInt() }
+            val right = with(density) { contentPadding.calculateRightPadding(layoutDirection).toPx().roundToInt() }
+            val bottom = with(density) { contentPadding.calculateBottomPadding().toPx().roundToInt() }
+            Box(modifier = Modifier.nestedScroll(nestedScrollConnection)) {
+                AndroidView(
+                    factory = { context ->
+                        screenModel.controllerBinding = PreMigrationListBinding.inflate(LayoutInflater.from(context))
+                        screenModel.adapter = MigrationSourceAdapter(screenModel.clickListener)
+                        screenModel.controllerBinding.recycler.adapter = screenModel.adapter
+                        screenModel.adapter?.isHandleDragEnabled = true
+                        screenModel.adapter?.fastScroller = screenModel.controllerBinding.fastScroller
+                        screenModel.controllerBinding.recycler.layoutManager = LinearLayoutManager(context)
+
+                        ViewCompat.setNestedScrollingEnabled(screenModel.controllerBinding.root, true)
+
+                        screenModel.controllerBinding.root
+                    },
+                    update = {
+                        screenModel.controllerBinding.recycler
+                            .updatePadding(
+                                left = left,
+                                top = top,
+                                right = right,
+                                bottom = bottom,
+                            )
+
+                        screenModel.controllerBinding.fastScroller
+                            .updateLayoutParams<ViewGroup.MarginLayoutParams> {
+                                leftMargin = left
+                                topMargin = top
+                                rightMargin = right
+                                bottomMargin = bottom
+                            }
+
+                        screenModel.adapter?.updateDataSet(items)
+                    },
+                )
+            }
+        }
+    }
+
+    companion object {
+        fun navigateToMigration(skipPre: Boolean, navigator: Navigator, mangaIds: List<Long>) {
+            navigator.push(
+                if (skipPre) {
+                    MigrationListScreen(
+                        MigrationProcedureConfig(mangaIds, null),
+                    )
+                } else {
+                    PreMigrationScreen(mangaIds)
+                },
+            )
+        }
+    }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationScreenModel.kt
new file mode 100644
index 000000000..f1e4f1169
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/design/PreMigrationScreenModel.kt
@@ -0,0 +1,143 @@
+package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
+
+import cafe.adriel.voyager.core.model.ScreenModel
+import cafe.adriel.voyager.core.model.coroutineScope
+import eu.davidea.flexibleadapter.FlexibleAdapter
+import eu.kanade.domain.UnsortedPreferences
+import eu.kanade.domain.source.service.SourcePreferences
+import eu.kanade.tachiyomi.databinding.PreMigrationListBinding
+import eu.kanade.tachiyomi.source.SourceManager
+import eu.kanade.tachiyomi.source.online.HttpSource
+import eu.kanade.tachiyomi.util.lang.launchIO
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class PreMigrationScreenModel(
+    private val sourceManager: SourceManager = Injekt.get(),
+    private val prefs: UnsortedPreferences = Injekt.get(),
+    private val sourcePreferences: SourcePreferences = Injekt.get(),
+) : ScreenModel {
+
+    private val _state = MutableStateFlow(emptyList<MigrationSourceItem>())
+    val state = _state.asStateFlow()
+
+    lateinit var controllerBinding: PreMigrationListBinding
+    var adapter: MigrationSourceAdapter? = null
+
+    val startMigration = MutableSharedFlow<String?>()
+
+    val listener = object : StartMigrationListener {
+        override fun startMigration(extraParam: String?) {
+            val listOfSources = adapter?.currentItems
+                ?.filterIsInstance<MigrationSourceItem>()
+                ?.filter {
+                    it.sourceEnabled
+                }
+                ?.joinToString("/") { it.source.id.toString() }
+                .orEmpty()
+
+            prefs.migrationSources().set(listOfSources)
+
+            coroutineScope.launch {
+                startMigration.emit(extraParam)
+            }
+        }
+    }
+    val clickListener = FlexibleAdapter.OnItemClickListener { _, position ->
+        val adapter = adapter ?: return@OnItemClickListener false
+        adapter.getItem(position)?.let {
+            it.sourceEnabled = !it.sourceEnabled
+        }
+        adapter.notifyItemChanged(position)
+        false
+    }
+
+    lateinit var dialog: MigrationBottomSheetDialog
+
+    init {
+        coroutineScope.launchIO {
+            val enabledSources = getEnabledSources()
+            _state.update { enabledSources }
+        }
+    }
+
+    /**
+     * Returns a list of enabled sources ordered by language and name.
+     *
+     * @return list containing enabled sources.
+     */
+    private fun getEnabledSources(): List<MigrationSourceItem> {
+        val languages = sourcePreferences.enabledLanguages().get()
+        val sourcesSaved = prefs.migrationSources().get().split("/")
+            .mapNotNull { it.toLongOrNull() }
+        val disabledSources = sourcePreferences.disabledSources().get()
+            .mapNotNull { it.toLongOrNull() }
+        val sources = sourceManager.getVisibleCatalogueSources()
+            .asSequence()
+            .filterIsInstance<HttpSource>()
+            .filter { it.lang in languages }
+            .sortedBy { "(${it.lang}) ${it.name}" }
+            .map {
+                MigrationSourceItem(
+                    it,
+                    isEnabled(
+                        sourcesSaved,
+                        disabledSources,
+                        it.id,
+                    ),
+                )
+            }
+            .toList()
+
+        return sources
+            .filter { it.sourceEnabled }
+            .sortedBy { sourcesSaved.indexOf(it.source.id) }
+            .plus(
+                sources.filterNot { it.sourceEnabled },
+            )
+    }
+
+    fun isEnabled(
+        sourcesSaved: List<Long>,
+        disabledSources: List<Long>,
+        id: Long,
+    ): Boolean {
+        return if (sourcesSaved.isEmpty()) {
+            id !in disabledSources
+        } else {
+            id in sourcesSaved
+        }
+    }
+
+    fun massSelect(selectAll: Boolean) {
+        val adapter = adapter ?: return
+        adapter.currentItems.forEach {
+            it.sourceEnabled = selectAll
+        }
+        adapter.notifyDataSetChanged()
+    }
+
+    fun matchSelection(matchEnabled: Boolean) {
+        val adapter = adapter ?: return
+        val enabledSources = if (matchEnabled) {
+            sourcePreferences.disabledSources().get().mapNotNull { it.toLongOrNull() }
+        } else {
+            sourcePreferences.pinnedSources().get().mapNotNull { it.toLongOrNull() }
+        }
+        val items = adapter.currentItems.toList()
+        items.forEach {
+            it.sourceEnabled = if (matchEnabled) {
+                it.source.id !in enabledSources
+            } else {
+                it.source.id in enabledSources
+            }
+        }
+        val sortedItems = items.sortedBy { it.source.name }.sortedBy { !it.sourceEnabled }
+        adapter.updateDataSet(sortedItems)
+    }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt
index 47a1c83dc..a557f8488 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListController.kt
@@ -1,84 +1,14 @@
 package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
 
 import android.os.Bundle
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.aspectRatio
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.ArrowForward
-import androidx.compose.material.icons.outlined.Close
-import androidx.compose.material.icons.outlined.ContentCopy
-import androidx.compose.material.icons.outlined.CopyAll
-import androidx.compose.material.icons.outlined.Done
-import androidx.compose.material.icons.outlined.DoneAll
-import androidx.compose.material.icons.outlined.MoreVert
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.produceState
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shadow
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.DpOffset
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
 import androidx.core.os.bundleOf
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import eu.kanade.domain.manga.model.Manga
-import eu.kanade.presentation.components.AppBar
-import eu.kanade.presentation.components.Badge
-import eu.kanade.presentation.components.BadgeGroup
-import eu.kanade.presentation.components.MangaCover
-import eu.kanade.presentation.components.Scaffold
-import eu.kanade.presentation.components.ScrollbarLazyColumn
-import eu.kanade.presentation.util.plus
-import eu.kanade.presentation.util.rememberResourceBitmapPainter
-import eu.kanade.presentation.util.topSmallPaddingValues
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.source.Source
-import eu.kanade.tachiyomi.ui.base.changehandler.OneWayFadeChangeHandler
-import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
-import eu.kanade.tachiyomi.ui.base.controller.pushController
-import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
-import eu.kanade.tachiyomi.ui.browse.migration.MigrationMangaDialog
-import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
-import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga.SearchResult
-import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
-import eu.kanade.tachiyomi.ui.manga.MangaController
-import eu.kanade.tachiyomi.util.lang.withIOContext
-import eu.kanade.tachiyomi.util.system.getParcelableCompat
-import eu.kanade.tachiyomi.util.system.toast
-import me.saket.cascade.CascadeDropdownMenu
+import cafe.adriel.voyager.navigator.Navigator
+import eu.kanade.tachiyomi.ui.base.controller.BasicFullComposeController
+import eu.kanade.tachiyomi.util.system.getSerializableCompat
 
 class MigrationListController(bundle: Bundle? = null) :
-    FullComposeController<MigrationListPresenter>(bundle) {
+    BasicFullComposeController(bundle) {
 
     constructor(config: MigrationProcedureConfig) : this(
         bundleOf(
@@ -86,414 +16,11 @@ class MigrationListController(bundle: Bundle? = null) :
         ),
     )
 
-    val config = args.getParcelableCompat<MigrationProcedureConfig>(CONFIG_EXTRA)
-
-    private var selectedMangaId: Long? = null
-    private var manualMigrations = 0
-
-    override fun createPresenter(): MigrationListPresenter {
-        return MigrationListPresenter(config!!)
-    }
+    val config = args.getSerializableCompat<MigrationProcedureConfig>(CONFIG_EXTRA)!!
 
     @Composable
     override fun ComposeContent() {
-        val items by presenter.migratingItems.collectAsState()
-        val migrationDone by presenter.migrationDone.collectAsState()
-        val unfinishedCount by presenter.unfinishedCount.collectAsState()
-        Scaffold(
-            topBar = { scrollBehavior ->
-                val titleString = stringResource(R.string.migration)
-                val title by produceState(initialValue = titleString, items, unfinishedCount, titleString) {
-                    withIOContext {
-                        value = "$titleString ($unfinishedCount/${items.size})"
-                    }
-                }
-                AppBar(
-                    title = title,
-                    actions = {
-                        IconButton(
-                            onClick = { openMigrationDialog(true) },
-                            enabled = migrationDone,
-                        ) {
-                            Icon(
-                                imageVector = if (items.size == 1) Icons.Outlined.ContentCopy else Icons.Outlined.CopyAll,
-                                contentDescription = stringResource(R.string.copy),
-                            )
-                        }
-                        IconButton(
-                            onClick = { openMigrationDialog(false) },
-                            enabled = migrationDone,
-                        ) {
-                            Icon(
-                                imageVector = if (items.size == 1) Icons.Outlined.Done else Icons.Outlined.DoneAll,
-                                contentDescription = stringResource(R.string.migrate),
-                            )
-                        }
-                    },
-                    scrollBehavior = scrollBehavior,
-                )
-            },
-        ) { contentPadding ->
-            ScrollbarLazyColumn(
-                contentPadding = contentPadding + topSmallPaddingValues,
-            ) {
-                items(items, key = { it.manga.id }) { migrationItem ->
-                    Row(
-                        Modifier
-                            .fillMaxWidth()
-                            .animateItemPlacement()
-                            .padding(horizontal = 16.dp),
-                        horizontalArrangement = Arrangement.SpaceBetween,
-                        verticalAlignment = Alignment.CenterVertically,
-                    ) {
-                        val result by migrationItem.searchResult.collectAsState()
-                        MigrationItem(
-                            modifier = Modifier
-                                .padding(top = 8.dp)
-                                .weight(1f),
-                            manga = migrationItem.manga,
-                            sourcesString = migrationItem.sourcesString,
-                            chapterInfo = migrationItem.chapterInfo,
-                            onClick = {
-                                router.pushController(
-                                    MangaController(
-                                        migrationItem.manga.id,
-                                        true,
-                                    ),
-                                )
-                            },
-                        )
-
-                        Icon(
-                            Icons.Outlined.ArrowForward,
-                            contentDescription = stringResource(R.string.migrating_to),
-                            modifier = Modifier.weight(0.2f),
-                        )
-
-                        MigrationItemResult(
-                            modifier = Modifier
-                                .padding(top = 8.dp)
-                                .weight(1f),
-                            migrationItem = migrationItem,
-                            result = result,
-                        )
-
-                        MigrationActionIcon(
-                            modifier = Modifier
-                                .weight(0.2f),
-                            result = result,
-                            skipManga = { presenter.removeManga(migrationItem.manga.id) },
-                            searchManually = {
-                                val manga = migrationItem.manga
-                                selectedMangaId = manga.id
-                                val sources = presenter.getMigrationSources()
-                                val validSources = if (sources.size == 1) {
-                                    sources
-                                } else {
-                                    sources.filter { it.id != manga.source }
-                                }
-                                val searchController = SearchController(manga, validSources)
-                                searchController.targetController = this@MigrationListController
-                                router.pushController(searchController)
-                            },
-                            migrateNow = {
-                                migrateManga(migrationItem.manga.id, false)
-                                manualMigrations++
-                            },
-                            copyNow = {
-                                migrateManga(migrationItem.manga.id, true)
-                                manualMigrations++
-                            },
-                        )
-                    }
-                }
-            }
-        }
-    }
-
-    @Composable
-    fun MigrationItem(
-        modifier: Modifier,
-        manga: Manga,
-        sourcesString: String,
-        chapterInfo: MigratingManga.ChapterInfo,
-        onClick: () -> Unit,
-    ) {
-        Column(
-            modifier
-                .widthIn(max = 150.dp)
-                .fillMaxWidth()
-                .clip(MaterialTheme.shapes.small)
-                .clickable(onClick = onClick)
-                .padding(4.dp),
-        ) {
-            val context = LocalContext.current
-            Box(
-                Modifier.fillMaxWidth()
-                    .aspectRatio(MangaCover.Book.ratio),
-            ) {
-                MangaCover.Book(
-                    modifier = Modifier
-                        .fillMaxWidth(),
-                    data = manga,
-                )
-                Box(
-                    modifier = Modifier
-                        .clip(RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp))
-                        .background(
-                            Brush.verticalGradient(
-                                0f to Color.Transparent,
-                                1f to Color(0xAA000000),
-                            ),
-                        )
-                        .fillMaxHeight(0.33f)
-                        .fillMaxWidth()
-                        .align(Alignment.BottomCenter),
-                )
-                Text(
-                    modifier = Modifier
-                        .padding(8.dp)
-                        .align(Alignment.BottomStart),
-                    text = manga.title.ifBlank { stringResource(R.string.unknown) },
-                    fontSize = 12.sp,
-                    lineHeight = 18.sp,
-                    maxLines = 2,
-                    overflow = TextOverflow.Ellipsis,
-                    style = MaterialTheme.typography.titleSmall.copy(
-                        color = Color.White,
-                        shadow = Shadow(
-                            color = Color.Black,
-                            blurRadius = 4f,
-                        ),
-                    ),
-                )
-                BadgeGroup(modifier = Modifier.padding(4.dp)) {
-                    Badge(text = "${chapterInfo.chapterCount}")
-                }
-            }
-            Text(
-                text = sourcesString,
-                modifier = Modifier.padding(top = 4.dp, bottom = 1.dp, start = 8.dp),
-                overflow = TextOverflow.Ellipsis,
-                maxLines = 1,
-                style = MaterialTheme.typography.titleSmall,
-            )
-
-            val formattedLatestChapter by produceState(initialValue = "") {
-                value = withIOContext {
-                    chapterInfo.getFormattedLatestChapter(context)
-                }
-            }
-            Text(
-                text = formattedLatestChapter,
-                modifier = Modifier.padding(top = 1.dp, bottom = 4.dp, start = 8.dp),
-                overflow = TextOverflow.Ellipsis,
-                maxLines = 1,
-                style = MaterialTheme.typography.bodyMedium,
-            )
-        }
-    }
-
-    @Composable
-    fun MigrationActionIcon(
-        modifier: Modifier,
-        result: SearchResult,
-        skipManga: () -> Unit,
-        searchManually: () -> Unit,
-        migrateNow: () -> Unit,
-        copyNow: () -> Unit,
-    ) {
-        var moreExpanded by remember { mutableStateOf(false) }
-        val closeMenu = { moreExpanded = false }
-
-        Box(modifier) {
-            if (result is SearchResult.Searching) {
-                IconButton(onClick = skipManga) {
-                    Icon(
-                        imageVector = Icons.Outlined.Close,
-                        contentDescription = stringResource(R.string.action_stop),
-                    )
-                }
-            } else if (result is SearchResult.Result || result is SearchResult.NotFound) {
-                IconButton(onClick = { moreExpanded = !moreExpanded }) {
-                    Icon(
-                        imageVector = Icons.Outlined.MoreVert,
-                        contentDescription = stringResource(R.string.abc_action_menu_overflow_description),
-                    )
-                }
-                CascadeDropdownMenu(
-                    expanded = moreExpanded,
-                    onDismissRequest = closeMenu,
-                    offset = DpOffset(8.dp, (-56).dp),
-                ) {
-                    DropdownMenuItem(
-                        text = { Text(stringResource(R.string.action_search_manually)) },
-                        onClick = {
-                            searchManually()
-                            closeMenu()
-                        },
-                    )
-                    DropdownMenuItem(
-                        text = { Text(stringResource(R.string.action_skip_manga)) },
-                        onClick = {
-                            skipManga()
-                            closeMenu()
-                        },
-                    )
-                    if (result is SearchResult.Result) {
-                        DropdownMenuItem(
-                            text = { Text(stringResource(R.string.action_migrate_now)) },
-                            onClick = {
-                                migrateNow()
-                                closeMenu()
-                            },
-                        )
-                        DropdownMenuItem(
-                            text = { Text(stringResource(R.string.action_copy_now)) },
-                            onClick = {
-                                copyNow()
-                                closeMenu()
-                            },
-                        )
-                    }
-                }
-            }
-        }
-    }
-
-    @Composable
-    fun MigrationItemResult(modifier: Modifier, migrationItem: MigratingManga, result: SearchResult) {
-        Box(modifier) {
-            when (result) {
-                SearchResult.Searching -> Box(
-                    modifier = Modifier
-                        .widthIn(max = 150.dp)
-                        .fillMaxWidth()
-                        .aspectRatio(MangaCover.Book.ratio),
-                    contentAlignment = Alignment.Center,
-                ) {
-                    CircularProgressIndicator()
-                }
-                SearchResult.NotFound -> Image(
-                    painter = rememberResourceBitmapPainter(id = R.drawable.cover_error),
-                    contentDescription = null,
-                    modifier = Modifier
-                        .matchParentSize()
-                        .clip(RoundedCornerShape(4.dp)),
-                    contentScale = ContentScale.Crop,
-                )
-                is SearchResult.Result -> {
-                    val item by produceState<Triple<Manga, MigratingManga.ChapterInfo, String>?>(
-                        initialValue = null,
-                        migrationItem,
-                        result,
-                    ) {
-                        value = withIOContext {
-                            val manga = presenter.getManga(result) ?: return@withIOContext null
-                            Triple(
-                                manga,
-                                presenter.getChapterInfo(result),
-                                presenter.getSourceName(manga),
-                            )
-                        }
-                    }
-                    if (item != null) {
-                        val (manga, chapterInfo, source) = item!!
-                        MigrationItem(
-                            modifier = Modifier,
-                            manga = manga,
-                            sourcesString = source,
-                            chapterInfo = chapterInfo,
-                            onClick = {
-                                router.pushController(
-                                    MangaController(
-                                        manga.id,
-                                        true,
-                                    ),
-                                )
-                            },
-                        )
-                    }
-                }
-            }
-        }
-    }
-
-    private fun noMigration() {
-        val res = resources
-        if (res != null) {
-            activity?.toast(
-                res.getQuantityString(
-                    R.plurals.manga_migrated,
-                    manualMigrations,
-                    manualMigrations,
-                ),
-            )
-        }
-        if (!presenter.hideNotFound) {
-            router.popCurrentController()
-        }
-    }
-
-    fun useMangaForMigration(manga: Manga, source: Source) {
-        presenter.useMangaForMigration(manga, source, selectedMangaId ?: return)
-    }
-
-    fun migrateMangas() {
-        presenter.migrateMangas()
-    }
-
-    fun copyMangas() {
-        presenter.copyMangas()
-    }
-
-    fun migrateManga(mangaId: Long, copy: Boolean) {
-        presenter.migrateManga(mangaId, copy)
-    }
-
-    fun removeManga(mangaId: Long) {
-        presenter.removeManga(mangaId)
-    }
-
-    fun sourceFinished() {
-        if (presenter.migratingItems.value.isEmpty()) noMigration()
-    }
-
-    fun navigateOut(manga: Manga?) {
-        if (manga != null) {
-            val newStack = router.backstack.filter {
-                it.controller !is MangaController &&
-                    it.controller !is MigrationListController &&
-                    it.controller !is PreMigrationController
-            } + MangaController(manga.id).withFadeTransaction()
-            router.setBackstack(newStack, OneWayFadeChangeHandler())
-            return
-        }
-        router.popCurrentController()
-    }
-
-    override fun handleBack(): Boolean {
-        activity?.let {
-            MaterialAlertDialogBuilder(it)
-                .setTitle(R.string.stop_migrating)
-                .setPositiveButton(R.string.action_stop) { _, _ ->
-                    router.popCurrentController()
-                }
-                .setNegativeButton(android.R.string.cancel, null)
-                .show()
-        }
-        return true
-    }
-
-    private fun openMigrationDialog(copy: Boolean) {
-        val totalManga = presenter.migratingItems.value.size
-        val mangaSkipped = presenter.mangasSkipped()
-        MigrationMangaDialog(
-            this,
-            copy,
-            totalManga,
-            mangaSkipped,
-        ).showDialog(router)
+        Navigator(screen = MigrationListScreen(config))
     }
 
     companion object {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListScreen.kt
new file mode 100644
index 000000000..b66957f6a
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListScreen.kt
@@ -0,0 +1,177 @@
+package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.platform.LocalContext
+import cafe.adriel.voyager.core.model.rememberScreenModel
+import cafe.adriel.voyager.core.screen.Screen
+import cafe.adriel.voyager.navigator.LocalNavigator
+import cafe.adriel.voyager.navigator.currentOrThrow
+import eu.kanade.presentation.browse.MigrationListScreen
+import eu.kanade.presentation.browse.components.MigrationExitDialog
+import eu.kanade.presentation.browse.components.MigrationMangaDialog
+import eu.kanade.presentation.util.LocalRouter
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.base.changehandler.OneWayFadeChangeHandler
+import eu.kanade.tachiyomi.ui.base.controller.pushController
+import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
+import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationScreen
+import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
+import eu.kanade.tachiyomi.ui.manga.MangaController
+import eu.kanade.tachiyomi.ui.manga.MangaScreen
+import eu.kanade.tachiyomi.util.lang.withUIContext
+import eu.kanade.tachiyomi.util.system.toast
+
+class MigrationListScreen(private val config: MigrationProcedureConfig) : Screen {
+
+    @Composable
+    override fun Content() {
+        val screenModel = rememberScreenModel { MigrationListScreenModel(config) }
+        val items by screenModel.migratingItems.collectAsState()
+        val migrationDone by screenModel.migrationDone.collectAsState()
+        val unfinishedCount by screenModel.unfinishedCount.collectAsState()
+        val dialog by screenModel.dialog.collectAsState()
+        val navigator = LocalNavigator.currentOrThrow
+        val router = LocalRouter.currentOrThrow
+        val context = LocalContext.current
+        LaunchedEffect(items) {
+            if (items.isEmpty()) {
+                val manualMigrations = screenModel.manualMigrations.value
+                context.toast(
+                    context.resources.getQuantityString(
+                        R.plurals.manga_migrated,
+                        manualMigrations,
+                        manualMigrations,
+                    ),
+                )
+                if (!screenModel.hideNotFound) {
+                    if (navigator.canPop) {
+                        navigator.pop()
+                    } else {
+                        router.popCurrentController()
+                    }
+                }
+            }
+        }
+
+        LaunchedEffect(screenModel) {
+            screenModel.navigateOut.collect {
+                if (navigator.canPop) {
+                    if (items.size == 1) {
+                        val hasDetails = navigator.items.any { it is MangaScreen }
+                        if (hasDetails) {
+                            val manga = (items.firstOrNull()?.searchResult?.value as? MigratingManga.SearchResult.Result)?.let {
+                                screenModel.getManga(it.id)
+                            }
+                            withUIContext {
+                                if (manga != null) {
+                                    val newStack = navigator.items.filter {
+                                        it !is MangaScreen &&
+                                            it !is MigrationListScreen &&
+                                            it !is PreMigrationScreen
+                                    } + MangaScreen(manga.id)
+                                    navigator replaceAll newStack.first()
+                                    navigator.push(newStack.drop(1))
+                                } else {
+                                    navigator.pop()
+                                }
+                            }
+                        }
+                    } else {
+                        withUIContext {
+                            navigator.pop()
+                        }
+                    }
+                } else {
+                    if (items.size == 1) {
+                        val hasDetails = router.backstack.any { it.controller is MangaController }
+                        if (hasDetails) {
+                            val manga = (items.firstOrNull()?.searchResult?.value as? MigratingManga.SearchResult.Result)?.let {
+                                screenModel.getManga(it.id)
+                            }
+                            withUIContext {
+                                if (manga != null) {
+                                    val newStack = router.backstack.filter {
+                                        it.controller !is MangaController &&
+                                            it.controller !is MigrationListController &&
+                                            it.controller !is PreMigrationController
+                                    } + MangaController(manga.id).withFadeTransaction()
+                                    router.setBackstack(newStack, OneWayFadeChangeHandler())
+                                } else {
+                                    router.popCurrentController()
+                                }
+                            }
+                        }
+                    } else {
+                        withUIContext {
+                            router.popCurrentController()
+                        }
+                    }
+                }
+            }
+        }
+        MigrationListScreen(
+            items = items,
+            migrationDone = migrationDone,
+            unfinishedCount = unfinishedCount,
+            getManga = screenModel::getManga,
+            getChapterInfo = screenModel::getChapterInfo,
+            getSourceName = screenModel::getSourceName,
+            onMigrationItemClick = {
+                navigator.push(MangaScreen(it.id, true))
+            },
+            openMigrationDialog = screenModel::openMigrateDialog,
+            skipManga = { screenModel.removeManga(it) },
+            searchManually = { migrationItem ->
+                val sources = screenModel.getMigrationSources()
+                val validSources = if (sources.size == 1) {
+                    sources
+                } else {
+                    sources.filter { it.id != migrationItem.manga.source }
+                }
+                val searchController = SearchController(migrationItem.manga, validSources)
+                searchController.useMangaForMigration = { manga, source ->
+                    screenModel.useMangaForMigration(context, manga, source, migrationItem.manga.id)
+                }
+                router.pushController(searchController)
+            },
+            migrateNow = { screenModel.migrateManga(it, false) },
+            copyNow = { screenModel.migrateManga(it, true) },
+        )
+
+        val onDismissRequest = { screenModel.dialog.value = null }
+        when (val dialog = dialog) {
+            is MigrationListScreenModel.Dialog.MigrateMangaDialog -> {
+                MigrationMangaDialog(
+                    onDismissRequest = onDismissRequest,
+                    copy = dialog.copy,
+                    mangaSet = dialog.mangaSet,
+                    mangaSkipped = dialog.mangaSkipped,
+                    copyManga = screenModel::copyMangas,
+                    migrateManga = screenModel::migrateMangas,
+                )
+            }
+            MigrationListScreenModel.Dialog.MigrationExitDialog -> {
+                MigrationExitDialog(
+                    onDismissRequest = onDismissRequest,
+                    exitMigration = {
+                        if (navigator.canPop) {
+                            navigator.pop()
+                        } else {
+                            router.popCurrentController()
+                        }
+                    },
+                )
+            }
+            null -> Unit
+        }
+
+        BackHandler(true) {
+            screenModel.dialog.value = MigrationListScreenModel.Dialog.MigrationExitDialog
+        }
+    }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListScreenModel.kt
similarity index 86%
rename from app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListPresenter.kt
rename to app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListScreenModel.kt
index 1396ee70a..a262ab196 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListPresenter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationListScreenModel.kt
@@ -1,7 +1,9 @@
 package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
 
-import android.os.Bundle
+import android.content.Context
 import android.widget.Toast
+import cafe.adriel.voyager.core.model.ScreenModel
+import cafe.adriel.voyager.core.model.coroutineScope
 import eu.kanade.domain.UnsortedPreferences
 import eu.kanade.domain.category.interactor.GetCategories
 import eu.kanade.domain.category.interactor.SetMangaCategories
@@ -30,10 +32,8 @@ import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.source.getNameForMangaInfo
 import eu.kanade.tachiyomi.source.online.all.EHentai
-import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
 import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
 import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga.SearchResult
-import eu.kanade.tachiyomi.ui.manga.MangaController
 import eu.kanade.tachiyomi.util.lang.launchIO
 import eu.kanade.tachiyomi.util.lang.withUIContext
 import eu.kanade.tachiyomi.util.system.logcat
@@ -42,10 +42,11 @@ import exh.eh.EHentaiThrottleManager
 import exh.smartsearch.SmartSearchEngine
 import exh.source.MERGED_SOURCE_ID
 import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.cancel
+import kotlinx.coroutines.currentCoroutineContext
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.sync.Semaphore
@@ -55,7 +56,7 @@ import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import java.util.concurrent.atomic.AtomicInteger
 
-class MigrationListPresenter(
+class MigrationListScreenModel(
     private val config: MigrationProcedureConfig,
     private val preferences: UnsortedPreferences = Injekt.get(),
     private val sourceManager: SourceManager = Injekt.get(),
@@ -74,56 +75,56 @@ class MigrationListPresenter(
     private val getTracks: GetTracks = Injekt.get(),
     private val insertTrack: InsertTrack = Injekt.get(),
     private val deleteTrack: DeleteTrack = Injekt.get(),
-) : BasePresenter<MigrationListController>() {
+) : ScreenModel {
 
     private val smartSearchEngine = SmartSearchEngine(config.extraSearchParams)
     private val throttleManager = EHentaiThrottleManager()
 
-    var migrationsJob: Job? = null
-        private set
-
     val migratingItems = MutableStateFlow<List<MigratingManga>>(emptyList())
     val migrationDone = MutableStateFlow(false)
     val unfinishedCount = MutableStateFlow(0)
 
+    val manualMigrations = MutableStateFlow(0)
+
     val hideNotFound = preferences.hideNotFoundMigration().get()
 
-    override fun onCreate(savedState: Bundle?) {
-        super.onCreate(savedState)
+    val navigateOut = MutableSharedFlow<Unit>()
 
-        if (migrationsJob?.isActive != true) {
-            migrationsJob = presenterScope.launchIO {
-                runMigrations(
-                    config.mangaIds
-                        .map {
-                            async {
-                                val manga = getManga.await(it) ?: return@async null
-                                MigratingManga(
-                                    manga = manga,
-                                    chapterInfo = getChapterInfo(it),
-                                    sourcesString = sourceManager.getOrStub(manga.source).getNameForMangaInfo(
-                                        if (manga.source == MERGED_SOURCE_ID) {
-                                            getMergedReferencesById.await(manga.id)
-                                                .map { sourceManager.getOrStub(it.mangaSourceId) }
-                                        } else {
-                                            null
-                                        },
-                                    ),
-                                    parentContext = presenterScope.coroutineContext,
-                                )
-                            }
+    val dialog = MutableStateFlow<Dialog?>(null)
+
+    init {
+        coroutineScope.launchIO {
+            runMigrations(
+                config.mangaIds
+                    .map {
+                        async {
+                            val manga = getManga.await(it) ?: return@async null
+                            MigratingManga(
+                                manga = manga,
+                                chapterInfo = getChapterInfo(it),
+                                sourcesString = sourceManager.getOrStub(manga.source).getNameForMangaInfo(
+                                    if (manga.source == MERGED_SOURCE_ID) {
+                                        getMergedReferencesById.await(manga.id)
+                                            .map { sourceManager.getOrStub(it.mangaSourceId) }
+                                    } else {
+                                        null
+                                    },
+                                ),
+                                parentContext = coroutineScope.coroutineContext,
+                            )
                         }
-                        .awaitAll()
-                        .filterNotNull()
-                        .also {
-                            migratingItems.value = it
-                        },
-                )
-            }
+                    }
+                    .awaitAll()
+                    .filterNotNull()
+                    .also {
+                        migratingItems.value = it
+                    },
+            )
         }
     }
 
-    suspend fun getManga(result: SearchResult.Result) = getManga.await(result.id)
+    suspend fun getManga(result: SearchResult.Result) = getManga(result.id)
+    suspend fun getManga(id: Long) = getManga.await(id)
     suspend fun getChapterInfo(result: SearchResult.Result) = getChapterInfo(result.id)
     suspend fun getChapterInfo(id: Long) = getChapterByMangaId.await(id).let { chapters ->
         MigratingManga.ChapterInfo(
@@ -146,7 +147,7 @@ class MigrationListPresenter(
 
         val sources = getMigrationSources()
         for (manga in mangas) {
-            if (migrationsJob?.isCancelled == true) {
+            if (!currentCoroutineContext().isActive) {
                 break
             }
             // in case it was removed
@@ -224,7 +225,7 @@ class MigrationListPresenter(
                                                 source.getChapterList(localManga.toSManga())
                                             }
                                         } catch (e: Exception) {
-                                            this@MigrationListPresenter.logcat(LogPriority.ERROR, e)
+                                            this@MigrationListScreenModel.logcat(LogPriority.ERROR, e)
                                             emptyList()
                                         }
                                         syncChaptersWithSource.await(chapters, localManga, source)
@@ -274,14 +275,13 @@ class MigrationListPresenter(
         }
     }
 
-    private suspend fun sourceFinished() {
+    private fun sourceFinished() {
         unfinishedCount.value = migratingItems.value.count {
             it.searchResult.value != SearchResult.Searching
         }
         if (allMangasDone()) {
             migrationDone.value = true
         }
-        withUIContext { view?.sourceFinished() }
     }
 
     fun allMangasDone() = migratingItems.value.all { it.searchResult.value != SearchResult.Searching } &&
@@ -383,11 +383,11 @@ class MigrationListPresenter(
         updateManga.awaitAll(listOfNotNull(mangaUpdate, prevMangaUpdate))
     }
 
-    fun useMangaForMigration(manga: Manga, source: Source, selectedMangaId: Long) {
+    fun useMangaForMigration(context: Context, manga: Manga, source: Source, selectedMangaId: Long) {
         val migratingManga = migratingItems.value.find { it.manga.id == selectedMangaId }
             ?: return
         migratingManga.searchResult.value = SearchResult.Searching
-        presenterScope.launchIO {
+        coroutineScope.launchIO {
             val result = migratingManga.migrationScope.async {
                 val localManga = networkToLocalManga.await(manga)
                 try {
@@ -413,14 +413,14 @@ class MigrationListPresenter(
             } else {
                 migratingManga.searchResult.value = SearchResult.NotFound
                 withUIContext {
-                    view?.activity?.toast(R.string.no_chapters_found_for_migration, Toast.LENGTH_LONG)
+                    context.toast(R.string.no_chapters_found_for_migration, Toast.LENGTH_LONG)
                 }
             }
         }
     }
 
     fun migrateMangas() {
-        presenterScope.launchIO {
+        coroutineScope.launchIO {
             migratingItems.value.forEach { manga ->
                 val searchResult = manga.searchResult.value
                 if (searchResult is SearchResult.Result) {
@@ -438,7 +438,7 @@ class MigrationListPresenter(
     }
 
     fun copyMangas() {
-        presenterScope.launchIO {
+        coroutineScope.launchIO {
             migratingItems.value.forEach { manga ->
                 val searchResult = manga.searchResult.value
                 if (searchResult is SearchResult.Result) {
@@ -455,26 +455,12 @@ class MigrationListPresenter(
     }
 
     private suspend fun navigateOut() {
-        val view = view ?: return
-        if (migratingItems.value.size == 1) {
-            val hasDetails = view.router.backstack.any { it.controller is MangaController }
-            if (hasDetails) {
-                val manga = (migratingItems.value.firstOrNull()?.searchResult?.value as? SearchResult.Result)?.let {
-                    getManga.await(it.id)
-                }
-                withUIContext {
-                    view.navigateOut(manga)
-                }
-                return
-            }
-        }
-        withUIContext {
-            view.navigateOut(null)
-        }
+        navigateOut.emit(Unit)
     }
 
     fun migrateManga(mangaId: Long, copy: Boolean) {
-        presenterScope.launchIO {
+        manualMigrations.value++
+        coroutineScope.launchIO {
             val manga = migratingItems.value.find { it.manga.id == mangaId }
                 ?: return@launchIO
 
@@ -491,7 +477,7 @@ class MigrationListPresenter(
     }
 
     fun removeManga(mangaId: Long) {
-        presenterScope.launchIO {
+        coroutineScope.launchIO {
             val item = migratingItems.value.find { it.manga.id == mangaId }
                 ?: return@launchIO
             if (migratingItems.value.size == 1) {
@@ -517,11 +503,25 @@ class MigrationListPresenter(
         }
     }
 
-    override fun onDestroy() {
-        super.onDestroy()
-        migrationsJob?.cancel()
+    override fun onDispose() {
+        super.onDispose()
         migratingItems.value.forEach {
             it.migrationScope.cancel()
         }
     }
+
+    fun openMigrateDialog(
+        copy: Boolean,
+    ) {
+        dialog.value = Dialog.MigrateMangaDialog(
+            copy,
+            migratingItems.value.size,
+            mangasSkipped(),
+        )
+    }
+
+    sealed class Dialog {
+        data class MigrateMangaDialog(val copy: Boolean, val mangaSet: Int, val mangaSkipped: Int) : Dialog()
+        object MigrationExitDialog : Dialog()
+    }
 }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcedureConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcedureConfig.kt
index a3d813bc5..c0469b885 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcedureConfig.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcedureConfig.kt
@@ -1,10 +1,8 @@
 package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
 
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
+import java.io.Serializable
 
-@Parcelize
 data class MigrationProcedureConfig(
     var mangaIds: List<Long>,
     val extraSearchParams: String?,
-) : Parcelable
+) : Serializable
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchController.kt
index e42be502c..f18132e98 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchController.kt
@@ -5,9 +5,9 @@ import androidx.core.os.bundleOf
 import eu.kanade.domain.manga.interactor.GetManga
 import eu.kanade.domain.manga.model.Manga
 import eu.kanade.tachiyomi.source.CatalogueSource
+import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.ui.base.controller.pushController
-import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListController
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchPresenter
 import kotlinx.coroutines.runBlocking
@@ -24,24 +24,23 @@ class SearchController(
         SOURCES to sources?.map { it.id }?.toLongArray(),
     ),
 ) {
-    constructor(targetController: MigrationListController?, mangaId: Long, sources: LongArray) :
+    constructor(mangaId: Long, sources: LongArray) :
         this(
             runBlocking {
                 Injekt.get<GetManga>()
                     .await(mangaId)
             },
             sources.map { Injekt.get<SourceManager>().getOrStub(it) }.filterIsInstance<CatalogueSource>(),
-        ) {
-        this.targetController = targetController
-    }
+        )
 
     @Suppress("unused")
     constructor(bundle: Bundle) : this(
-        null,
         bundle.getLong(OLD_MANGA),
         bundle.getLongArray(SOURCES) ?: LongArray(0),
     )
 
+    var useMangaForMigration: ((Manga, Source) -> Unit)? = null
+
     /**
      * Called when controller is initialized.
      */
@@ -58,10 +57,9 @@ class SearchController(
     }
 
     override fun onMangaClick(manga: Manga) {
-        val migrationListController = targetController as MigrationListController
         val sourceManager = Injekt.get<SourceManager>()
         val source = sourceManager.get(manga.source) ?: return
-        migrationListController.useMangaForMigration(manga, source)
+        useMangaForMigration?.let { it(manga, source) }
         router.popCurrentController()
     }
 
@@ -73,7 +71,7 @@ class SearchController(
     override fun onTitleClick(source: CatalogueSource) {
         presenter.sourcePreferences.lastUsedSource().set(source.id)
 
-        router.pushController(SourceSearchController(targetController as? MigrationListController ?: return, manga!!, source, presenter.query))
+        router.pushController(SourceSearchController(manga!!, source, presenter.query).also { it.useMangaForMigration = useMangaForMigration })
     }
 
     companion object {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchController.kt
index 2cbc76112..9c0cf59a3 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchController.kt
@@ -7,9 +7,9 @@ import androidx.core.os.bundleOf
 import eu.kanade.domain.manga.model.Manga
 import eu.kanade.presentation.browse.SourceSearchScreen
 import eu.kanade.tachiyomi.source.CatalogueSource
+import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.source.online.HttpSource
-import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListController
 import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
 import eu.kanade.tachiyomi.ui.webview.WebViewActivity
 import uy.kohesive.injekt.Injekt
@@ -19,15 +19,15 @@ class SourceSearchController(
     bundle: Bundle,
 ) : BrowseSourceController(bundle) {
 
-    constructor(targetController: MigrationListController, manga: Manga, source: CatalogueSource, searchQuery: String? = null) : this(
+    constructor(manga: Manga, source: CatalogueSource, searchQuery: String? = null) : this(
         bundleOf(
             SOURCE_ID_KEY to source.id,
             MANGA_KEY to manga,
             SEARCH_QUERY_KEY to searchQuery,
         ),
-    ) {
-        this.targetController = targetController
-    }
+    )
+
+    var useMangaForMigration: ((Manga, Source) -> Unit)? = null
 
     @Composable
     override fun ComposeContent() {
@@ -37,11 +37,11 @@ class SourceSearchController(
             onFabClick = { filterSheet?.show() },
             // SY -->
             onMangaClick = { manga ->
-                val migrationListController = targetController as? MigrationListController ?: return@SourceSearchScreen
                 val sourceManager = Injekt.get<SourceManager>()
                 val source = sourceManager.get(manga.source) ?: return@SourceSearchScreen
-                migrationListController.useMangaForMigration(manga, source)
-                router.popToTag(MigrationListController.TAG)
+                useMangaForMigration?.let { it(manga, source) }
+                router.popCurrentController()
+                router.popCurrentController()
             },
             // SY <--
             onWebViewClick = f@{