Эх сурвалжийг харах

Dedupe SearchScreenModels

arkon 1 жил өмнө
parent
commit
ca789dca0e

+ 2 - 2
app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt

@@ -10,8 +10,8 @@ import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
 import eu.kanade.presentation.browse.components.GlobalSearchResultItem
 import eu.kanade.presentation.browse.components.GlobalSearchToolbar
 import eu.kanade.tachiyomi.source.CatalogueSource
-import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreenModel
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
+import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
 import eu.kanade.tachiyomi.util.system.LocaleHelper
 import tachiyomi.domain.manga.model.Manga
@@ -19,7 +19,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
 
 @Composable
 fun GlobalSearchScreen(
-    state: GlobalSearchScreenModel.State,
+    state: SearchScreenModel.State,
     navigateUp: () -> Unit,
     onChangeSearchQuery: (String?) -> Unit,
     onSearch: (String) -> Unit,

+ 4 - 3
app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt

@@ -4,14 +4,15 @@ import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
 import eu.kanade.presentation.browse.components.GlobalSearchToolbar
 import eu.kanade.tachiyomi.source.CatalogueSource
-import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreenModel
+import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
 import tachiyomi.domain.manga.model.Manga
 import tachiyomi.presentation.core.components.material.Scaffold
 
 @Composable
 fun MigrateSearchScreen(
-    state: MigrateSearchScreenModel.State,
+    state: SearchScreenModel.State,
+    fromSourceId: Long?,
     navigateUp: () -> Unit,
     onChangeSearchQuery: (String?) -> Unit,
     onSearch: (String) -> Unit,
@@ -40,7 +41,7 @@ fun MigrateSearchScreen(
         },
     ) { paddingValues ->
         GlobalSearchContent(
-            fromSourceId = state.manga?.source,
+            fromSourceId = fromSourceId,
             items = state.filteredItems,
             contentPadding = paddingValues,
             getManga = getManga,

+ 10 - 7
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt

@@ -10,7 +10,6 @@ import eu.kanade.presentation.browse.MigrateSearchScreen
 import eu.kanade.presentation.util.Screen
 import eu.kanade.tachiyomi.ui.manga.MangaScreen
 
-// TODO: this should probably be merged with GlobalSearchScreen somehow to dedupe logic
 class MigrateSearchScreen(private val mangaId: Long) : Screen() {
 
     @Composable
@@ -20,8 +19,12 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() {
         val screenModel = rememberScreenModel { MigrateSearchScreenModel(mangaId = mangaId) }
         val state by screenModel.state.collectAsState()
 
+        val dialogScreenModel = rememberScreenModel { MigrateSearchScreenDialogScreenModel(mangaId = mangaId) }
+        val dialogState by dialogScreenModel.state.collectAsState()
+
         MigrateSearchScreen(
             state = state,
+            fromSourceId = dialogState.manga?.source,
             navigateUp = navigator::pop,
             onChangeSearchQuery = screenModel::updateSearchQuery,
             onSearch = screenModel::search,
@@ -29,19 +32,19 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() {
             onChangeSearchFilter = screenModel::setSourceFilter,
             onToggleResults = screenModel::toggleFilterResults,
             onClickSource = {
-                navigator.push(SourceSearchScreen(state.manga!!, it.id, state.searchQuery))
+                navigator.push(SourceSearchScreen(dialogState.manga!!, it.id, state.searchQuery))
             },
-            onClickItem = { screenModel.setDialog(MigrateSearchScreenModel.Dialog.Migrate(it)) },
+            onClickItem = { dialogScreenModel.setDialog(MigrateSearchScreenDialogScreenModel.Dialog.Migrate(it)) },
             onLongClickItem = { navigator.push(MangaScreen(it.id, true)) },
         )
 
-        when (val dialog = state.dialog) {
-            is MigrateSearchScreenModel.Dialog.Migrate -> {
+        when (val dialog = dialogState.dialog) {
+            is MigrateSearchScreenDialogScreenModel.Dialog.Migrate -> {
                 MigrateDialog(
-                    oldManga = state.manga!!,
+                    oldManga = dialogState.manga!!,
                     newManga = dialog.manga,
                     screenModel = rememberScreenModel { MigrateDialogScreenModel() },
-                    onDismissRequest = { screenModel.setDialog(null) },
+                    onDismissRequest = { dialogScreenModel.setDialog(null) },
                     onClickTitle = {
                         navigator.push(MangaScreen(dialog.manga.id, true))
                     },

+ 43 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenDialogScreenModel.kt

@@ -0,0 +1,43 @@
+package eu.kanade.tachiyomi.ui.browse.migration.search
+
+import androidx.compose.runtime.Immutable
+import cafe.adriel.voyager.core.model.StateScreenModel
+import cafe.adriel.voyager.core.model.coroutineScope
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import tachiyomi.domain.manga.interactor.GetManga
+import tachiyomi.domain.manga.model.Manga
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+class MigrateSearchScreenDialogScreenModel(
+    val mangaId: Long,
+    getManga: GetManga = Injekt.get(),
+) : StateScreenModel<MigrateSearchScreenDialogScreenModel.State>(State()) {
+
+    init {
+        coroutineScope.launch {
+            val manga = getManga.await(mangaId)!!
+
+            mutableState.update {
+                it.copy(manga = manga)
+            }
+        }
+    }
+
+    fun setDialog(dialog: Dialog?) {
+        mutableState.update {
+            it.copy(dialog = dialog)
+        }
+    }
+
+    @Immutable
+    data class State(
+        val manga: Manga? = null,
+        val dialog: Dialog? = null,
+    )
+
+    sealed class Dialog {
+        data class Migrate(val manga: Manga) : Dialog()
+    }
+}

+ 3 - 57
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenModel.kt

@@ -1,29 +1,26 @@
 package eu.kanade.tachiyomi.ui.browse.migration.search
 
-import androidx.compose.runtime.Immutable
 import cafe.adriel.voyager.core.model.coroutineScope
 import eu.kanade.tachiyomi.source.CatalogueSource
-import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
 import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 import tachiyomi.domain.manga.interactor.GetManga
-import tachiyomi.domain.manga.model.Manga
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 
 class MigrateSearchScreenModel(
     val mangaId: Long,
     getManga: GetManga = Injekt.get(),
-) : SearchScreenModel<MigrateSearchScreenModel.State>(State()) {
+) : SearchScreenModel() {
 
     init {
         coroutineScope.launch {
             val manga = getManga.await(mangaId)!!
 
             mutableState.update {
-                it.copy(manga = manga, searchQuery = manga.title)
+                it.copy(fromSourceId = manga.source, searchQuery = manga.title)
             }
 
             search(manga.title)
@@ -35,61 +32,10 @@ class MigrateSearchScreenModel(
             .filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources }
             .sortedWith(
                 compareBy(
-                    { it.id != state.value.manga!!.source },
+                    { it.id != state.value.fromSourceId },
                     { "${it.id}" !in pinnedSources },
                     { "${it.name.lowercase()} (${it.lang})" },
                 ),
             )
     }
-
-    override fun updateSearchQuery(query: String?) {
-        mutableState.update {
-            it.copy(searchQuery = query)
-        }
-    }
-
-    override fun updateItems(items: Map<CatalogueSource, SearchItemResult>) {
-        mutableState.update {
-            it.copy(items = items)
-        }
-    }
-
-    override fun getItems(): Map<CatalogueSource, SearchItemResult> {
-        return mutableState.value.items
-    }
-
-    override fun setSourceFilter(filter: SourceFilter) {
-        mutableState.update { it.copy(sourceFilter = filter) }
-    }
-
-    override fun toggleFilterResults() {
-        mutableState.update {
-            it.copy(onlyShowHasResults = !it.onlyShowHasResults)
-        }
-    }
-
-    fun setDialog(dialog: Dialog?) {
-        mutableState.update {
-            it.copy(dialog = dialog)
-        }
-    }
-
-    @Immutable
-    data class State(
-        val manga: Manga? = null,
-        val dialog: Dialog? = null,
-
-        val searchQuery: String? = null,
-        val sourceFilter: SourceFilter = SourceFilter.PinnedOnly,
-        val onlyShowHasResults: Boolean = false,
-        val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
-    ) {
-        val progress: Int = items.count { it.value !is SearchItemResult.Loading }
-        val total: Int = items.size
-        val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
-    }
-
-    sealed class Dialog {
-        data class Migrate(val manga: Manga) : Dialog()
-    }
 }

+ 1 - 41
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreenModel.kt

@@ -1,13 +1,11 @@
 package eu.kanade.tachiyomi.ui.browse.source.globalsearch
 
-import androidx.compose.runtime.Immutable
 import eu.kanade.tachiyomi.source.CatalogueSource
-import kotlinx.coroutines.flow.update
 
 class GlobalSearchScreenModel(
     initialQuery: String = "",
     initialExtensionFilter: String? = null,
-) : SearchScreenModel<GlobalSearchScreenModel.State>(State(searchQuery = initialQuery)) {
+) : SearchScreenModel(State(searchQuery = initialQuery)) {
 
     init {
         extensionFilter = initialExtensionFilter
@@ -20,42 +18,4 @@ class GlobalSearchScreenModel(
         return super.getEnabledSources()
             .filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources }
     }
-
-    override fun updateSearchQuery(query: String?) {
-        mutableState.update {
-            it.copy(searchQuery = query)
-        }
-    }
-
-    override fun updateItems(items: Map<CatalogueSource, SearchItemResult>) {
-        mutableState.update {
-            it.copy(items = items)
-        }
-    }
-
-    override fun getItems(): Map<CatalogueSource, SearchItemResult> {
-        return mutableState.value.items
-    }
-
-    override fun setSourceFilter(filter: SourceFilter) {
-        mutableState.update { it.copy(sourceFilter = filter) }
-    }
-
-    override fun toggleFilterResults() {
-        mutableState.update {
-            it.copy(onlyShowHasResults = !it.onlyShowHasResults)
-        }
-    }
-
-    @Immutable
-    data class State(
-        val searchQuery: String? = null,
-        val sourceFilter: SourceFilter = SourceFilter.PinnedOnly,
-        val onlyShowHasResults: Boolean = false,
-        val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
-    ) {
-        val progress: Int = items.count { it.value !is SearchItemResult.Loading }
-        val total: Int = items.size
-        val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
-    }
 }

+ 44 - 16
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt

@@ -1,10 +1,9 @@
 package eu.kanade.tachiyomi.ui.browse.source.globalsearch
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
+import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.produceState
 import cafe.adriel.voyager.core.model.StateScreenModel
-import eu.kanade.domain.manga.interactor.UpdateManga
 import eu.kanade.domain.manga.model.toDomainManga
 import eu.kanade.domain.source.service.SourcePreferences
 import eu.kanade.presentation.util.ioCoroutineScope
@@ -16,6 +15,7 @@ import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import tachiyomi.core.util.lang.awaitSingle
@@ -27,15 +27,14 @@ import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import java.util.concurrent.Executors
 
-abstract class SearchScreenModel<T>(
-    initialState: T,
+abstract class SearchScreenModel(
+    initialState: State = State(),
     private val sourcePreferences: SourcePreferences = Injekt.get(),
     private val sourceManager: SourceManager = Injekt.get(),
     private val extensionManager: ExtensionManager = Injekt.get(),
     private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
     private val getManga: GetManga = Injekt.get(),
-    private val updateManga: UpdateManga = Injekt.get(),
-) : StateScreenModel<T>(initialState) {
+) : StateScreenModel<SearchScreenModel.State>(initialState) {
 
     private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher()
     private var searchJob: Job? = null
@@ -55,7 +54,7 @@ abstract class SearchScreenModel<T>(
     }
 
     @Composable
-    fun getManga(initialManga: Manga): State<Manga> {
+    fun getManga(initialManga: Manga): androidx.compose.runtime.State<Manga> {
         return produceState(initialValue = initialManga) {
             getManga.subscribe(initialManga.url, initialManga.source)
                 .filterNotNull()
@@ -95,19 +94,25 @@ abstract class SearchScreenModel<T>(
             .filter { it in enabledSources }
     }
 
-    abstract fun updateSearchQuery(query: String?)
-
-    abstract fun updateItems(items: Map<CatalogueSource, SearchItemResult>)
-
-    abstract fun getItems(): Map<CatalogueSource, SearchItemResult>
+    fun updateSearchQuery(query: String?) {
+        mutableState.update {
+            it.copy(searchQuery = query)
+        }
+    }
 
-    private fun getAndUpdateItems(function: (Map<CatalogueSource, SearchItemResult>) -> Map<CatalogueSource, SearchItemResult>) {
-        updateItems(function(getItems()))
+    fun getItems(): Map<CatalogueSource, SearchItemResult> {
+        return mutableState.value.items
     }
 
-    abstract fun setSourceFilter(filter: SourceFilter)
+    fun setSourceFilter(filter: SourceFilter) {
+        mutableState.update { it.copy(sourceFilter = filter) }
+    }
 
-    abstract fun toggleFilterResults()
+    fun toggleFilterResults() {
+        mutableState.update {
+            it.copy(onlyShowHasResults = !it.onlyShowHasResults)
+        }
+    }
 
     fun search(query: String) {
         if (this.query == query) return
@@ -147,6 +152,29 @@ abstract class SearchScreenModel<T>(
                 .awaitAll()
         }
     }
+
+    private fun updateItems(items: Map<CatalogueSource, SearchItemResult>) {
+        mutableState.update {
+            it.copy(items = items)
+        }
+    }
+
+    private fun getAndUpdateItems(function: (Map<CatalogueSource, SearchItemResult>) -> Map<CatalogueSource, SearchItemResult>) {
+        updateItems(function(getItems()))
+    }
+
+    @Immutable
+    data class State(
+        val fromSourceId: Long? = null,
+        val searchQuery: String? = null,
+        val sourceFilter: SourceFilter = SourceFilter.PinnedOnly,
+        val onlyShowHasResults: Boolean = false,
+        val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
+    ) {
+        val progress: Int = items.count { it.value !is SearchItemResult.Loading }
+        val total: Int = items.size
+        val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
+    }
 }
 
 enum class SourceFilter {