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.api.get
import uy.kohesive.injekt.injectLazy 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 { object MigrationFlags {
private const val CHAPTERS = 0b00001 private const val CHAPTERS = 0b00001
@ -23,9 +39,6 @@ object MigrationFlags {
private val getTracks: GetTracks = Injekt.get() private val getTracks: GetTracks = Injekt.get()
private val downloadCache: DownloadCache by injectLazy() 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 { fun hasChapters(value: Int): Boolean {
return value and CHAPTERS != 0 return value and CHAPTERS != 0
} }
@ -46,34 +59,35 @@ object MigrationFlags {
return value and DELETE_DOWNLOADED != 0 return value and DELETE_DOWNLOADED != 0
} }
fun getEnabledFlagsPositions(value: Int): List<Int> { /** Returns information about applicable flags with default selections. */
return flags.mapIndexedNotNull { index, flag -> if (value and flag != 0) index else null } 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 (manga != null) {
if (runBlocking { getTracks.await(manga.id) }.isNotEmpty()) { if (runBlocking { getTracks.await(manga.id) }.isNotEmpty()) {
titles.add(R.string.track) flags += MigrationFlag.create(TRACK, defaultSelectedBitMap, R.string.track)
enableFlags.add(TRACK)
} }
if (manga.hasCustomCover(coverCache)) { if (manga.hasCustomCover(coverCache)) {
titles.add(R.string.custom_cover) flags += MigrationFlag.create(CUSTOM_COVER, defaultSelectedBitMap, R.string.custom_cover)
enableFlags.add(CUSTOM_COVER)
} }
if (downloadCache.getDownloadCount(manga) > 0) { if (downloadCache.getDownloadCount(manga) > 0) {
titles.add(R.string.delete_downloaded) flags += MigrationFlag.create(DELETE_DOWNLOADED, defaultSelectedBitMap, R.string.delete_downloaded)
enableFlags.add(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.Immutable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
@ -74,15 +73,8 @@ internal fun MigrateDialog(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
val activeFlags = remember { MigrationFlags.getEnabledFlagsPositions(screenModel.migrateFlags.get()) } val flags = remember { MigrationFlags.getFlags(oldManga, screenModel.migrateFlags.get()) }
val items = remember { val selectedFlags = remember { flags.map { it.isDefaultSelected }.toMutableStateList() }
MigrationFlags.titles(oldManga)
.map { context.getString(it) }
.toList()
}
val selected = remember {
mutableStateListOf(*List(items.size) { i -> activeFlags.contains(i) }.toTypedArray())
}
if (state.isMigrating) { if (state.isMigrating) {
LoadingScreen( LoadingScreen(
@ -99,18 +91,16 @@ internal fun MigrateDialog(
Column( Column(
modifier = Modifier.verticalScroll(rememberScrollState()), modifier = Modifier.verticalScroll(rememberScrollState()),
) { ) {
items.forEachIndexed { index, title -> flags.forEachIndexed { index, flag ->
val onChange: () -> Unit = { val onChange = { selectedFlags[index] = !selectedFlags[index] }
selected[index] = !selected[index]
}
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onChange), .clickable(onClick = onChange),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Checkbox(checked = selected[index], onCheckedChange = { onChange() }) Checkbox(checked = selectedFlags[index], onCheckedChange = { onChange() })
Text(text = title) Text(text = context.getString(flag.titleId))
} }
} }
} }
@ -133,7 +123,12 @@ internal fun MigrateDialog(
TextButton( TextButton(
onClick = { onClick = {
scope.launchIO { scope.launchIO {
screenModel.migrateManga(oldManga, newManga, false) screenModel.migrateManga(
oldManga,
newManga,
false,
MigrationFlags.getSelectedFlagsBitMap(selectedFlags, flags),
)
withUIContext { onPopScreen() } withUIContext { onPopScreen() }
} }
}, },
@ -143,12 +138,13 @@ internal fun MigrateDialog(
TextButton( TextButton(
onClick = { onClick = {
scope.launchIO { scope.launchIO {
val selectedIndices = mutableListOf<Int>() screenModel.migrateManga(
selected.fastForEachIndexed { i, b -> if (b) selectedIndices.add(i) } oldManga,
val newValue = newManga,
MigrationFlags.getFlagsFromPositions(selectedIndices.toTypedArray()) true,
screenModel.migrateFlags.set(newValue) MigrationFlags.getSelectedFlagsBitMap(selectedFlags, flags),
screenModel.migrateManga(oldManga, newManga, true) )
withUIContext { onPopScreen() } withUIContext { onPopScreen() }
} }
}, },
@ -184,7 +180,13 @@ internal class MigrateDialogScreenModel(
Injekt.get<TrackManager>().services.filterIsInstance<EnhancedTrackService>() 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 source = sourceManager.get(newManga.source) ?: return
val prevSource = sourceManager.get(oldManga.source) val prevSource = sourceManager.get(oldManga.source)
@ -200,6 +202,7 @@ internal class MigrateDialogScreenModel(
newManga = newManga, newManga = newManga,
sourceChapters = chapters, sourceChapters = chapters,
replace = replace, replace = replace,
flags = flags,
) )
} catch (_: Throwable) { } catch (_: Throwable) {
// Explicitly stop if an error occurred; the dialog normally gets popped at the end // Explicitly stop if an error occurred; the dialog normally gets popped at the end
@ -215,9 +218,8 @@ internal class MigrateDialogScreenModel(
newManga: Manga, newManga: Manga,
sourceChapters: List<SChapter>, sourceChapters: List<SChapter>,
replace: Boolean, replace: Boolean,
flags: Int,
) { ) {
val flags = migrateFlags.get()
val migrateChapters = MigrationFlags.hasChapters(flags) val migrateChapters = MigrationFlags.hasChapters(flags)
val migrateCategories = MigrationFlags.hasCategories(flags) val migrateCategories = MigrationFlags.hasCategories(flags)
val migrateTracks = MigrationFlags.hasTracks(flags) val migrateTracks = MigrationFlags.hasTracks(flags)