Browse Source

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.
Mekanik 1 year ago
parent
commit
4b7acdb022

+ 37 - 23
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt

@@ -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 }
-    }
-
-    fun getFlagsFromPositions(positions: Array<Int>): Int {
-        val fold = positions.fold(0) { accumulated, position -> accumulated or enableFlags[position] }
-        enableFlags.clear()
-        return fold
-    }
+    /** 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 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
     }
 }

+ 29 - 27
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt

@@ -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)