Fix migration flags usage (incorrect defaults and copy mode) (#9805)

* Fix migration flags usage (incorect defaults and copy mode)

* Remove unused logcat import left from testing.
This commit is contained in:
Mekanik 2023-08-05 08:49:22 -07:00 committed by GitHub
parent af0fdfa3b7
commit 4b7acdb022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 50 deletions

View File

@ -11,6 +11,22 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
data class MigrationFlag(
val flag: Int,
val isDefaultSelected: Boolean,
val titleId: Int,
) {
companion object {
fun create(flag: Int, defaultSelectionMap: Int, titleId: Int): MigrationFlag {
return MigrationFlag(
flag = flag,
isDefaultSelected = defaultSelectionMap and flag != 0,
titleId = titleId,
)
}
}
}
object MigrationFlags {
private const val CHAPTERS = 0b00001
@ -23,9 +39,6 @@ object MigrationFlags {
private val getTracks: GetTracks = Injekt.get()
private val downloadCache: DownloadCache by injectLazy()
val flags get() = arrayOf(CHAPTERS, CATEGORIES, TRACK, CUSTOM_COVER, DELETE_DOWNLOADED)
private var enableFlags = emptyList<Int>().toMutableList()
fun hasChapters(value: Int): Boolean {
return value and CHAPTERS != 0
}
@ -46,34 +59,35 @@ object MigrationFlags {
return value and DELETE_DOWNLOADED != 0
}
fun getEnabledFlagsPositions(value: Int): List<Int> {
return flags.mapIndexedNotNull { index, flag -> if (value and flag != 0) index else null }
}
/** Returns information about applicable flags with default selections. */
fun getFlags(manga: Manga?, defaultSelectedBitMap: Int): List<MigrationFlag> {
val flags = mutableListOf<MigrationFlag>()
flags += MigrationFlag.create(CHAPTERS, defaultSelectedBitMap, R.string.chapters)
flags += MigrationFlag.create(CATEGORIES, defaultSelectedBitMap, R.string.categories)
fun getFlagsFromPositions(positions: Array<Int>): Int {
val fold = positions.fold(0) { accumulated, position -> accumulated or enableFlags[position] }
enableFlags.clear()
return fold
}
fun titles(manga: Manga?): Array<Int> {
enableFlags.add(CHAPTERS)
enableFlags.add(CATEGORIES)
val titles = arrayOf(R.string.chapters, R.string.categories).toMutableList()
if (manga != null) {
if (runBlocking { getTracks.await(manga.id) }.isNotEmpty()) {
titles.add(R.string.track)
enableFlags.add(TRACK)
flags += MigrationFlag.create(TRACK, defaultSelectedBitMap, R.string.track)
}
if (manga.hasCustomCover(coverCache)) {
titles.add(R.string.custom_cover)
enableFlags.add(CUSTOM_COVER)
flags += MigrationFlag.create(CUSTOM_COVER, defaultSelectedBitMap, R.string.custom_cover)
}
if (downloadCache.getDownloadCount(manga) > 0) {
titles.add(R.string.delete_downloaded)
enableFlags.add(DELETE_DOWNLOADED)
flags += MigrationFlag.create(DELETE_DOWNLOADED, defaultSelectedBitMap, R.string.delete_downloaded)
}
}
return titles.toTypedArray()
return flags
}
/** Returns a bit map of selected flags. */
fun getSelectedFlagsBitMap(
selectedFlags: List<Boolean>,
flags: List<MigrationFlag>,
): Int {
return selectedFlags
.zip(flags)
.filter { (isSelected, _) -> isSelected }
.map { (_, flag) -> flag.flag }
.reduceOrNull { acc, mask -> acc or mask } ?: 0
}
}

View File

@ -19,15 +19,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import cafe.adriel.voyager.core.model.StateScreenModel
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.manga.interactor.UpdateManga
@ -74,15 +73,8 @@ internal fun MigrateDialog(
val scope = rememberCoroutineScope()
val state by screenModel.state.collectAsState()
val activeFlags = remember { MigrationFlags.getEnabledFlagsPositions(screenModel.migrateFlags.get()) }
val items = remember {
MigrationFlags.titles(oldManga)
.map { context.getString(it) }
.toList()
}
val selected = remember {
mutableStateListOf(*List(items.size) { i -> activeFlags.contains(i) }.toTypedArray())
}
val flags = remember { MigrationFlags.getFlags(oldManga, screenModel.migrateFlags.get()) }
val selectedFlags = remember { flags.map { it.isDefaultSelected }.toMutableStateList() }
if (state.isMigrating) {
LoadingScreen(
@ -99,18 +91,16 @@ internal fun MigrateDialog(
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
items.forEachIndexed { index, title ->
val onChange: () -> Unit = {
selected[index] = !selected[index]
}
flags.forEachIndexed { index, flag ->
val onChange = { selectedFlags[index] = !selectedFlags[index] }
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onChange),
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(checked = selected[index], onCheckedChange = { onChange() })
Text(text = title)
Checkbox(checked = selectedFlags[index], onCheckedChange = { onChange() })
Text(text = context.getString(flag.titleId))
}
}
}
@ -133,7 +123,12 @@ internal fun MigrateDialog(
TextButton(
onClick = {
scope.launchIO {
screenModel.migrateManga(oldManga, newManga, false)
screenModel.migrateManga(
oldManga,
newManga,
false,
MigrationFlags.getSelectedFlagsBitMap(selectedFlags, flags),
)
withUIContext { onPopScreen() }
}
},
@ -143,12 +138,13 @@ internal fun MigrateDialog(
TextButton(
onClick = {
scope.launchIO {
val selectedIndices = mutableListOf<Int>()
selected.fastForEachIndexed { i, b -> if (b) selectedIndices.add(i) }
val newValue =
MigrationFlags.getFlagsFromPositions(selectedIndices.toTypedArray())
screenModel.migrateFlags.set(newValue)
screenModel.migrateManga(oldManga, newManga, true)
screenModel.migrateManga(
oldManga,
newManga,
true,
MigrationFlags.getSelectedFlagsBitMap(selectedFlags, flags),
)
withUIContext { onPopScreen() }
}
},
@ -184,7 +180,13 @@ internal class MigrateDialogScreenModel(
Injekt.get<TrackManager>().services.filterIsInstance<EnhancedTrackService>()
}
suspend fun migrateManga(oldManga: Manga, newManga: Manga, replace: Boolean) {
suspend fun migrateManga(
oldManga: Manga,
newManga: Manga,
replace: Boolean,
flags: Int,
) {
migrateFlags.set(flags)
val source = sourceManager.get(newManga.source) ?: return
val prevSource = sourceManager.get(oldManga.source)
@ -200,6 +202,7 @@ internal class MigrateDialogScreenModel(
newManga = newManga,
sourceChapters = chapters,
replace = replace,
flags = flags,
)
} catch (_: Throwable) {
// Explicitly stop if an error occurred; the dialog normally gets popped at the end
@ -215,9 +218,8 @@ internal class MigrateDialogScreenModel(
newManga: Manga,
sourceChapters: List<SChapter>,
replace: Boolean,
flags: Int,
) {
val flags = migrateFlags.get()
val migrateChapters = MigrationFlags.hasChapters(flags)
val migrateCategories = MigrationFlags.hasCategories(flags)
val migrateTracks = MigrationFlags.hasTracks(flags)