Cleanup migrate manga dialog and related code (#2156)

This commit is contained in:
AntsyLich
2025-05-31 21:03:39 +06:00
committed by GitHub
parent 5919f34fc9
commit 2b126f1ff5
24 changed files with 510 additions and 525 deletions
@@ -0,0 +1,167 @@
package mihon.feature.migration.dialog
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.util.fastForEach
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import eu.kanade.domain.manga.model.hasCustomCover
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import kotlinx.coroutines.flow.update
import mihon.domain.migration.models.MigrationFlag
import mihon.domain.migration.usecases.MigrateMangaUseCase
import mihon.feature.common.utils.getLabel
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.LoadingScreen
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Composable
internal fun Screen.MigrateMangaDialog(
current: Manga,
target: Manga,
onClickTitle: () -> Unit,
onDismissRequest: () -> Unit,
onComplete: () -> Unit = onDismissRequest,
) {
val scope = rememberCoroutineScope()
val screenModel = rememberScreenModel { MigrateDialogScreenModel(current, target) }
val state by screenModel.state.collectAsState()
if (state.isMigrating) {
LoadingScreen(
modifier = Modifier.background(MaterialTheme.colorScheme.background.copy(alpha = 0.7f)),
)
return
}
AlertDialog(
onDismissRequest = onDismissRequest,
title = {
Text(text = stringResource(MR.strings.migration_dialog_what_to_include))
},
text = {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
state.applicableFlags.fastForEach { flag ->
LabeledCheckbox(
label = stringResource(flag.getLabel()),
checked = flag in state.selectedFlags,
onCheckedChange = { screenModel.toggleSelection(flag) },
)
}
}
},
confirmButton = {
FlowRow(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
) {
TextButton(
onClick = {
onDismissRequest()
onClickTitle()
},
) {
Text(text = stringResource(MR.strings.action_show_manga))
}
Spacer(modifier = Modifier.weight(1f))
TextButton(
onClick = {
scope.launchIO {
screenModel.migrateManga(replace = false)
withUIContext { onComplete() }
}
},
) {
Text(text = stringResource(MR.strings.copy))
}
TextButton(
onClick = {
scope.launchIO {
screenModel.migrateManga(replace = true)
withUIContext { onComplete() }
}
},
) {
Text(text = stringResource(MR.strings.migrate))
}
}
},
)
}
private class MigrateDialogScreenModel(
private val current: Manga,
private val target: Manga,
private val sourcePreference: SourcePreferences = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val migrateManga: MigrateMangaUseCase = Injekt.get(),
) : StateScreenModel<MigrateDialogScreenModel.State>(State()) {
init {
val applicableFlags = buildList {
MigrationFlag.entries.forEach {
val applicable = when (it) {
MigrationFlag.CHAPTER -> true
MigrationFlag.CATEGORY -> true
MigrationFlag.CUSTOM_COVER -> current.hasCustomCover(coverCache)
MigrationFlag.NOTES -> current.notes.isNotBlank()
MigrationFlag.REMOVE_DOWNLOAD -> downloadManager.getDownloadCount(current) > 0
}
if (applicable) add(it)
}
}
val selectedFlags = sourcePreference.migrationFlags().get()
mutableState.update { it.copy(applicableFlags = applicableFlags, selectedFlags = selectedFlags) }
}
fun toggleSelection(flag: MigrationFlag) {
mutableState.update {
val selectedFlags = it.selectedFlags.toMutableSet()
.apply { if (contains(flag)) remove(flag) else add(flag) }
.toSet()
it.copy(selectedFlags = selectedFlags)
}
}
suspend fun migrateManga(replace: Boolean) {
sourcePreference.migrationFlags().set(state.value.selectedFlags)
mutableState.update { it.copy(isMigrating = true) }
migrateManga(current, target, replace)
mutableState.update { it.copy(isMigrating = false) }
}
data class State(
val applicableFlags: List<MigrationFlag> = emptyList(),
val selectedFlags: Set<MigrationFlag> = emptySet(),
val isMigrating: Boolean = false,
)
}