Browse Source

Use IO dispatcher for some screen model work

Not sure if this is an ideal approach. If it is, we could migrate more usages to this.
arkon 2 years ago
parent
commit
18f9e5ba6b

+ 4 - 4
app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt

@@ -31,11 +31,11 @@ import androidx.lifecycle.asFlow
 import androidx.work.WorkInfo
 import androidx.work.WorkQuery
 import cafe.adriel.voyager.core.model.ScreenModel
-import cafe.adriel.voyager.core.model.coroutineScope
 import cafe.adriel.voyager.core.model.rememberScreenModel
 import cafe.adriel.voyager.navigator.LocalNavigator
 import cafe.adriel.voyager.navigator.currentOrThrow
 import eu.kanade.presentation.util.Screen
+import eu.kanade.presentation.util.ioCoroutineScope
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.util.system.workManager
 import kotlinx.coroutines.flow.SharingStarted
@@ -128,19 +128,19 @@ object WorkerInfoScreen : Screen() {
             .getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
             .asFlow()
             .map(::constructString)
-            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "")
+            .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
 
         val running = workManager
             .getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING))
             .asFlow()
             .map(::constructString)
-            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "")
+            .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
 
         val enqueued = workManager
             .getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.ENQUEUED))
             .asFlow()
             .map(::constructString)
-            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "")
+            .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
 
         private fun constructString(list: List<WorkInfo>) = buildString {
             if (list.isEmpty()) {

+ 20 - 0
app/src/main/java/eu/kanade/presentation/util/Navigator.kt

@@ -3,12 +3,20 @@ package eu.kanade.presentation.util
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ProvidableCompositionLocal
 import androidx.compose.runtime.staticCompositionLocalOf
+import cafe.adriel.voyager.core.model.ScreenModel
+import cafe.adriel.voyager.core.model.ScreenModelStore
 import cafe.adriel.voyager.core.screen.Screen
 import cafe.adriel.voyager.core.screen.ScreenKey
 import cafe.adriel.voyager.core.screen.uniqueScreenKey
 import cafe.adriel.voyager.core.stack.StackEvent
 import cafe.adriel.voyager.navigator.Navigator
 import cafe.adriel.voyager.transitions.ScreenTransition
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.plus
 import soup.compose.material.motion.animation.materialSharedAxisX
 import soup.compose.material.motion.animation.rememberSlideDistance
 
@@ -28,6 +36,18 @@ abstract class Screen : Screen {
     override val key: ScreenKey = uniqueScreenKey
 }
 
+/**
+ * A variant of ScreenModel.coroutineScope except with the IO dispatcher instead of the
+ * main dispatcher.
+ */
+val ScreenModel.ioCoroutineScope: CoroutineScope
+    get() = ScreenModelStore.getOrPutDependency(
+        screenModel = this,
+        name = "ScreenModelIoCoroutineScope",
+        factory = { key -> CoroutineScope(Dispatchers.IO + SupervisorJob()) + CoroutineName(key) },
+        onDispose = { scope -> scope.cancel() },
+    )
+
 interface AssistContentScreen {
     fun onProvideAssistUrl(): String?
 }

+ 4 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt

@@ -18,6 +18,7 @@ import eu.kanade.domain.manga.interactor.UpdateManga
 import eu.kanade.domain.manga.model.toDomainManga
 import eu.kanade.domain.source.service.SourcePreferences
 import eu.kanade.domain.track.model.toDomainTrack
+import eu.kanade.presentation.util.ioCoroutineScope
 import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.track.EnhancedTrackService
 import eu.kanade.tachiyomi.data.track.TrackManager
@@ -125,12 +126,12 @@ class BrowseSourceScreenModel(
                         .filter { localManga ->
                             !sourcePreferences.hideInLibraryItems().get() || !localManga.favorite
                         }
-                        .stateIn(coroutineScope)
+                        .stateIn(ioCoroutineScope)
                 }
             }
-                .cachedIn(coroutineScope)
+                .cachedIn(ioCoroutineScope)
         }
-        .stateIn(coroutineScope, SharingStarted.Lazily, emptyFlow())
+        .stateIn(ioCoroutineScope, SharingStarted.Lazily, emptyFlow())
 
     fun getColumnsPreference(orientation: Int): GridCells {
         val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE

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

@@ -4,10 +4,10 @@ import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.produceState
 import cafe.adriel.voyager.core.model.StateScreenModel
-import cafe.adriel.voyager.core.model.coroutineScope
 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
 import eu.kanade.tachiyomi.extension.ExtensionManager
 import eu.kanade.tachiyomi.source.CatalogueSource
 import kotlinx.coroutines.asCoroutineDispatcher
@@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import tachiyomi.core.util.lang.awaitSingle
-import tachiyomi.core.util.lang.withIOContext
 import tachiyomi.domain.manga.interactor.GetManga
 import tachiyomi.domain.manga.interactor.NetworkToLocalManga
 import tachiyomi.domain.manga.model.Manga
@@ -94,7 +93,7 @@ abstract class SearchScreenModel<T>(
 
     abstract fun getItems(): Map<CatalogueSource, SearchItemResult>
 
-    fun getAndUpdateItems(function: (Map<CatalogueSource, SearchItemResult>) -> Map<CatalogueSource, SearchItemResult>) {
+    private fun getAndUpdateItems(function: (Map<CatalogueSource, SearchItemResult>) -> Map<CatalogueSource, SearchItemResult>) {
         updateItems(function(getItems()))
     }
 
@@ -106,7 +105,7 @@ abstract class SearchScreenModel<T>(
         val initialItems = getSelectedSources().associateWith { SearchItemResult.Loading }
         updateItems(initialItems)
 
-        coroutineScope.launch {
+        ioCoroutineScope.launch {
             sources
                 .map { source ->
                     async {
@@ -115,10 +114,8 @@ abstract class SearchScreenModel<T>(
                                 source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
                             }
 
-                            val titles = withIOContext {
-                                page.mangas.map {
-                                    networkToLocalManga.await(it.toDomainManga(source.id))
-                                }
+                            val titles = page.mangas.map {
+                                networkToLocalManga.await(it.toDomainManga(source.id))
                             }
 
                             getAndUpdateItems { items ->
@@ -129,7 +126,7 @@ abstract class SearchScreenModel<T>(
                         } catch (e: Exception) {
                             getAndUpdateItems { items ->
                                 val mutableMap = items.toMutableMap()
-                                mutableMap[source] = SearchItemResult.Error(throwable = e)
+                                mutableMap[source] = SearchItemResult.Error(e)
                                 mutableMap.toSortedMap(sortComparator(mutableMap))
                             }
                         }