diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index 9167fca8b..61afab701 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -6,7 +6,6 @@ import com.hippo.unifile.UniFile import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn -import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.source.Source import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope @@ -69,7 +68,6 @@ class DownloadCache( private val context: Context, private val provider: DownloadProvider, private val sourceManager: SourceManager, - private val extensionManager: ExtensionManager, private val storageManager: StorageManager, ) { @@ -356,7 +354,6 @@ class DownloadCache( // Try to wait until extensions and sources have loaded var sources = emptyList() withTimeoutOrNull(30.seconds) { - extensionManager.isInitialized.first { it } sourceManager.isInitialized.first { it } sources = getSources() diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 6a8f49597..949de947d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -23,9 +23,10 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import logcat.LogPriority @@ -55,19 +56,18 @@ class ExtensionManager( val scope = CoroutineScope(SupervisorJob()) - private val _isInitialized = MutableStateFlow(false) - val isInitialized: StateFlow = _isInitialized.asStateFlow() + private val isInitialized = MutableStateFlow(false) private val iconMap = mutableMapOf() private val installedExtensionMapFlow = MutableStateFlow(emptyMap()) - val installedExtensionsFlow = installedExtensionMapFlow.mapExtensions(scope) + val installedExtensionsFlow = installedExtensionMapFlow.mapExtensionsOnceInitialized() private val availableExtensionMapFlow = MutableStateFlow(emptyMap()) val availableExtensionsFlow = availableExtensionMapFlow.mapExtensions(scope) private val untrustedExtensionMapFlow = MutableStateFlow(emptyMap()) - val untrustedExtensionsFlow = untrustedExtensionMapFlow.mapExtensions(scope) + val untrustedExtensionsFlow = untrustedExtensionMapFlow.mapExtensionsOnceInitialized() init { scope.launch(Dispatchers.IO) { @@ -79,7 +79,7 @@ class ExtensionManager( private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages.isSet() fun getExtensionPackage(sourceId: Long): String? { - return installedExtensionsFlow.value.find { extension -> + return installedExtensionMapFlow.value.values.find { extension -> extension.sources.any { it.id == sourceId } } ?.pkgName @@ -128,7 +128,7 @@ class ExtensionManager( .filterIsInstance() .associate { it.extension.pkgName to it.extension } - _isInitialized.value = true + isInitialized.value = true } /** @@ -378,4 +378,12 @@ class ExtensionManager( private fun StateFlow>.mapExtensions(scope: CoroutineScope): StateFlow> { return map { it.values.toList() }.stateIn(scope, SharingStarted.Lazily, value.values.toList()) } + + /** + * Extensions are loaded in the background, and [stateIn] would replay the empty list it was + * seeded with at construction, so this only starts emitting once that finished. + */ + private fun StateFlow>.mapExtensionsOnceInitialized(): Flow> { + return onStart { isInitialized.first { it } }.map { it.values.toList() } + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt index eb33ffde1..6a753c53a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt @@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -48,8 +47,6 @@ class AndroidSourceManager( init { scope.launch { - extensionManager.isInitialized.first { it } - extensionManager.installedExtensionsFlow .collectLatest { extensions -> val mutableMap = ConcurrentHashMap( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt index 5d9049d14..71d603479 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchViewModel.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -98,7 +99,7 @@ abstract class SearchViewModel( ) } - private fun getSelectedSources(): List { + private suspend fun getSelectedSources(): List { val enabledSources = getEnabledSources() val filter = extensionFilter @@ -106,7 +107,7 @@ abstract class SearchViewModel( return enabledSources } - return extensionManager.installedExtensionsFlow.value + return extensionManager.installedExtensionsFlow.first() .filter { it.pkgName == filter } .flatMap { it.sources } .filter { it in enabledSources } @@ -139,23 +140,23 @@ abstract class SearchViewModel( searchJob?.cancel() - val sources = getSelectedSources() - - // Reuse previous results if possible - if (sameQuery) { - val existingResults = state.value.items - updateItems( - sources - .associateWith { existingResults[it] ?: SearchItemResult.Loading }, - ) - } else { - updateItems( - sources - .associateWith { SearchItemResult.Loading }, - ) - } - searchJob = viewModelScope.launchIO { + val sources = getSelectedSources() + + // Reuse previous results if possible + if (sameQuery) { + val existingResults = state.value.items + updateItems( + sources + .associateWith { existingResults[it] ?: SearchItemResult.Loading }, + ) + } else { + updateItems( + sources + .associateWith { SearchItemResult.Loading }, + ) + } + sources.map { source -> async { if (state.value.items[source] !is SearchItemResult.Loading) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt index 038983c7c..fe1d33bd2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.createFileInCacheDir import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast +import kotlinx.coroutines.flow.first import kotlinx.datetime.TimeZone import kotlinx.datetime.offsetAt import kotlinx.datetime.toLocalDateTime @@ -62,10 +63,10 @@ class CrashLogUtil( """.trimIndent() } - private fun getExtensionsInfo(): String? { + private suspend fun getExtensionsInfo(): String? { val availableExtensions = extensionManager.availableExtensionsFlow.value.associateBy { it.pkgName } - val extensionInfoList = extensionManager.installedExtensionsFlow.value + val extensionInfoList = extensionManager.installedExtensionsFlow.first() .sortedBy { it.name } .mapNotNull { val availableExtension = availableExtensions[it.pkgName]