Improve migration copy and migrate functions

This commit is contained in:
Jobobby04 2023-04-16 14:07:47 -04:00
parent d8c2baa135
commit 4212d155ce
4 changed files with 104 additions and 30 deletions

View File

@ -0,0 +1,41 @@
package eu.kanade.presentation.browse.components
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.window.DialogProperties
import eu.kanade.tachiyomi.R
@Composable
fun MigrationProgressDialog(
progress: Float,
exitMigration: () -> Unit,
) {
AlertDialog(
onDismissRequest = {},
confirmButton = {
TextButton(onClick = exitMigration) {
Text(text = stringResource(R.string.action_cancel))
}
},
text = {
if (!progress.isNaN()) {
val progressAnimated by animateFloatAsState(
targetValue = progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
)
LinearProgressIndicator(progressAnimated)
}
},
properties = DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false,
),
)
}

View File

@ -14,12 +14,15 @@ import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.MigrationListScreen import eu.kanade.presentation.browse.MigrationListScreen
import eu.kanade.presentation.browse.components.MigrationExitDialog import eu.kanade.presentation.browse.components.MigrationExitDialog
import eu.kanade.presentation.browse.components.MigrationMangaDialog import eu.kanade.presentation.browse.components.MigrationMangaDialog
import eu.kanade.presentation.browse.components.MigrationProgressDialog
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationScreen import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationScreen
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.util.overEq
import exh.util.underEq
import tachiyomi.core.util.lang.withUIContext import tachiyomi.core.util.lang.withUIContext
class MigrationListScreen(private val config: MigrationProcedureConfig) : Screen() { class MigrationListScreen(private val config: MigrationProcedureConfig) : Screen() {
@ -34,6 +37,7 @@ class MigrationListScreen(private val config: MigrationProcedureConfig) : Screen
val migrationDone by screenModel.migrationDone.collectAsState() val migrationDone by screenModel.migrationDone.collectAsState()
val unfinishedCount by screenModel.unfinishedCount.collectAsState() val unfinishedCount by screenModel.unfinishedCount.collectAsState()
val dialog by screenModel.dialog.collectAsState() val dialog by screenModel.dialog.collectAsState()
val migrateProgress by screenModel.migratingProgress.collectAsState()
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(items) { LaunchedEffect(items) {
@ -135,6 +139,13 @@ class MigrationListScreen(private val config: MigrationProcedureConfig) : Screen
null -> Unit null -> Unit
} }
if (!migrateProgress.isNaN() && migrateProgress overEq 0f && migrateProgress underEq 1f) {
MigrationProgressDialog(
progress = migrateProgress,
exitMigration = screenModel::cancelMigrate,
)
}
BackHandler(true) { BackHandler(true) {
screenModel.dialog.value = MigrationListScreenModel.Dialog.MigrationExitDialog screenModel.dialog.value = MigrationListScreenModel.Dialog.MigrationExitDialog
} }

View File

@ -20,10 +20,12 @@ import exh.eh.EHentaiThrottleManager
import exh.smartsearch.SmartSearchEngine import exh.smartsearch.SmartSearchEngine
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
@ -92,6 +94,10 @@ class MigrationListScreenModel(
val dialog = MutableStateFlow<Dialog?>(null) val dialog = MutableStateFlow<Dialog?>(null)
val migratingProgress = MutableStateFlow(Float.MAX_VALUE)
private var migrateJob: Job? = null
init { init {
coroutineScope.launchIO { coroutineScope.launchIO {
runMigrations( runMigrations(
@ -425,40 +431,56 @@ class MigrationListScreenModel(
} }
fun migrateMangas() { fun migrateMangas() {
coroutineScope.launchIO { migrateMangas(true)
migratingItems.value.orEmpty().forEach { manga ->
val searchResult = manga.searchResult.value
if (searchResult is SearchResult.Result) {
val toMangaObj = getManga.await(searchResult.id) ?: return@forEach
migrateMangaInternal(
manga.manga,
toMangaObj,
true,
)
}
}
navigateOut()
}
} }
fun copyMangas() { fun copyMangas() {
coroutineScope.launchIO { migrateMangas(false)
migratingItems.value.orEmpty().forEach { manga -> }
val searchResult = manga.searchResult.value
if (searchResult is SearchResult.Result) { private fun migrateMangas(replace: Boolean) {
val toMangaObj = getManga.await(searchResult.id) ?: return@forEach dialog.value = null
migrateMangaInternal( migrateJob = coroutineScope.launchIO {
manga.manga, migratingProgress.value = 0f
toMangaObj, val items = migratingItems.value.orEmpty()
false, try {
) items.forEachIndexed { index, manga ->
try {
ensureActive()
val toMangaObj = manga.searchResult.value.let {
if (it is SearchResult.Result) {
getManga.await(it.id)
} else {
null
}
}
if (toMangaObj != null) {
migrateMangaInternal(
manga.manga,
toMangaObj,
replace,
)
}
} catch (e: Exception) {
if (e is CancellationException) throw e
logcat(LogPriority.WARN, throwable = e)
}
migratingProgress.value = index.toFloat() / items.size
} }
navigateOut()
} finally {
migratingProgress.value = Float.MAX_VALUE
migrateJob = null
} }
navigateOut()
} }
} }
fun cancelMigrate() {
migrateJob?.cancel()
migrateJob = null
}
private suspend fun navigateOut() { private suspend fun navigateOut() {
navigateOut.emit(Unit) navigateOut.emit(Unit)
} }

View File

@ -1,9 +1,9 @@
package exh.util package exh.util
infix fun Int.over(other: Int) = this > other infix fun <T : Comparable<T>> T.over(other: T) = this > other
infix fun Int.overEq(other: Int) = this >= other infix fun <T : Comparable<T>> T.overEq(other: T) = this >= other
infix fun Int.under(other: Int) = this < other infix fun <T : Comparable<T>> T.under(other: T) = this < other
infix fun Int.underEq(other: Int) = this <= other infix fun <T : Comparable<T>> T.underEq(other: T) = this <= other