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 = { // KMK --> // Allow `migrate` mangas when using `Bulk-favorite` // onDismissRequest() // KMK <-- 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, sourcePreference: SourcePreferences = Injekt.get(), private val coverCache: CoverCache = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(), private val migrateManga: MigrateMangaUseCase = Injekt.get(), ) : StateScreenModel(State()) { init { val applicableFlags = buildList { MigrationFlag.entries.forEach { val applicable = when (it) { MigrationFlag.CHAPTER -> true MigrationFlag.CATEGORY -> true // KMK --> MigrationFlag.TRACK -> true // KMK <-- MigrationFlag.CUSTOM_COVER -> current.hasCustomCover(coverCache) MigrationFlag.NOTES -> current.notes.isNotBlank() MigrationFlag.REMOVE_DOWNLOAD -> downloadManager.getDownloadCount(current) > 0 // KMK --> MigrationFlag.EXTRA -> true // KMK <-- } 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) { // KMK --> // sourcePreference.migrationFlags().set(state.value.selectedFlags) // KMK <-- mutableState.update { it.copy(isMigrating = true) } migrateManga(current, target, replace, /* KMK --> */ state.value.selectedFlags /* KMK <-- */) mutableState.update { it.copy(isMigrating = false) } } data class State( val applicableFlags: List = emptyList(), val selectedFlags: Set = emptySet(), val isMigrating: Boolean = false, ) }