Wait for extensions to load before the list is propagated (#3841)
This commit is contained in:
@@ -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<Source>()
|
||||
withTimeoutOrNull(30.seconds) {
|
||||
extensionManager.isInitialized.first { it }
|
||||
sourceManager.isInitialized.first { it }
|
||||
|
||||
sources = getSources()
|
||||
|
||||
@@ -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<Boolean> = _isInitialized.asStateFlow()
|
||||
private val isInitialized = MutableStateFlow(false)
|
||||
|
||||
private val iconMap = mutableMapOf<String, Drawable>()
|
||||
|
||||
private val installedExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Installed>())
|
||||
val installedExtensionsFlow = installedExtensionMapFlow.mapExtensions(scope)
|
||||
val installedExtensionsFlow = installedExtensionMapFlow.mapExtensionsOnceInitialized()
|
||||
|
||||
private val availableExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Available>())
|
||||
val availableExtensionsFlow = availableExtensionMapFlow.mapExtensions(scope)
|
||||
|
||||
private val untrustedExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Untrusted>())
|
||||
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<LoadResult.Untrusted>()
|
||||
.associate { it.extension.pkgName to it.extension }
|
||||
|
||||
_isInitialized.value = true
|
||||
isInitialized.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,4 +378,12 @@ class ExtensionManager(
|
||||
private fun <T : Extension> StateFlow<Map<String, T>>.mapExtensions(scope: CoroutineScope): StateFlow<List<T>> {
|
||||
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 <T : Extension> StateFlow<Map<String, T>>.mapExtensionsOnceInitialized(): Flow<List<T>> {
|
||||
return onStart { isInitialized.first { it } }.map { it.values.toList() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Long, Source>(
|
||||
|
||||
+19
-18
@@ -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<Source> {
|
||||
private suspend fun getSelectedSources(): List<Source> {
|
||||
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) {
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user