ExtensionPresenter: Fix crash on first launch (#7685)

* Revert "Use SharedFlow for available extensions to always emit the value (#7609)"

This reverts commit 73901f50c0.

* ExtensionPresenter: Explicitly set refreshing status

* Scope coroutines job to presenter

* cleanup

* fix toast
This commit is contained in:
Ivan Iskandar 2022-08-05 02:44:37 +07:00 committed by GitHub
parent afceac15c8
commit b3426f37e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 35 deletions

View File

@ -15,15 +15,13 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
import eu.kanade.tachiyomi.extension.util.ExtensionLoader import eu.kanade.tachiyomi.extension.util.ExtensionLoader
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.launchNow import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.preference.plusAssign import eu.kanade.tachiyomi.util.preference.plusAssign
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import logcat.LogPriority import logcat.LogPriority
import rx.Observable import rx.Observable
@ -96,15 +94,15 @@ class ExtensionManager(
var availableExtensions = emptyList<Extension.Available>() var availableExtensions = emptyList<Extension.Available>()
private set(value) { private set(value) {
field = value field = value
availableExtensionsFlow.tryEmit(field) availableExtensionsFlow.value = field
updatedInstalledExtensionsStatuses(value) updatedInstalledExtensionsStatuses(value)
setupAvailableExtensionsSourcesDataMap(value) setupAvailableExtensionsSourcesDataMap(value)
} }
private val availableExtensionsFlow = MutableSharedFlow<List<Extension.Available>>(replay = 1) private val availableExtensionsFlow = MutableStateFlow(availableExtensions)
fun getAvailableExtensionsFlow(): Flow<List<Extension.Available>> { fun getAvailableExtensionsFlow(): StateFlow<List<Extension.Available>> {
return availableExtensionsFlow.asSharedFlow() return availableExtensionsFlow.asStateFlow()
} }
private var availableExtensionsSourcesData: Map<Long, SourceData> = mapOf() private var availableExtensionsSourcesData: Map<Long, SourceData> = mapOf()
@ -156,18 +154,16 @@ class ExtensionManager(
/** /**
* Finds the available extensions in the [api] and updates [availableExtensions]. * Finds the available extensions in the [api] and updates [availableExtensions].
*/ */
fun findAvailableExtensions() { suspend fun findAvailableExtensions() {
launchNow { val extensions: List<Extension.Available> = try {
val extensions: List<Extension.Available> = try { api.findExtensions()
api.findExtensions() } catch (e: Exception) {
} catch (e: Exception) { logcat(LogPriority.ERROR, e)
logcat(LogPriority.ERROR, e) withUIContext { context.toast(R.string.extension_api_error) }
context.toast(R.string.extension_api_error) emptyList()
emptyList()
}
availableExtensions = extensions
} }
availableExtensions = extensions
} }
/** /**

View File

@ -36,8 +36,6 @@ class ExtensionsPresenter(
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
extensionManager.findAvailableExtensions()
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
val extensionMapper: (Map<String, InstallStep>) -> ((Extension) -> ExtensionUiModel) = { map -> val extensionMapper: (Map<String, InstallStep>) -> ((Extension) -> ExtensionUiModel) = { map ->
{ {
@ -71,13 +69,13 @@ class ExtensionsPresenter(
} }
} }
launchIO { presenterScope.launchIO {
combine( combine(
_query, _query,
getExtensions.subscribe(), getExtensions.subscribe(),
_currentDownloads, _currentDownloads,
) { query, (updates, installed, available, untrusted), downloads -> ) { query, (_updates, _installed, _available, _untrusted), downloads ->
val languagesWithExtensions = available val languagesWithExtensions = _available
.filter(queryFilter(query)) .filter(queryFilter(query))
.groupBy { LocaleHelper.getSourceDisplayName(it.lang, context) } .groupBy { LocaleHelper.getSourceDisplayName(it.lang, context) }
.toSortedMap() .toSortedMap()
@ -90,14 +88,14 @@ class ExtensionsPresenter(
val items = mutableListOf<ExtensionUiModel>() val items = mutableListOf<ExtensionUiModel>()
val updates = updates.filter(queryFilter(query)).map(extensionMapper(downloads)) val updates = _updates.filter(queryFilter(query)).map(extensionMapper(downloads))
if (updates.isNotEmpty()) { if (updates.isNotEmpty()) {
items.add(ExtensionUiModel.Header.Resource(R.string.ext_updates_pending)) items.add(ExtensionUiModel.Header.Resource(R.string.ext_updates_pending))
items.addAll(updates) items.addAll(updates)
} }
val installed = installed.filter(queryFilter(query)).map(extensionMapper(downloads)) val installed = _installed.filter(queryFilter(query)).map(extensionMapper(downloads))
val untrusted = untrusted.filter(queryFilter(query)).map(extensionMapper(downloads)) val untrusted = _untrusted.filter(queryFilter(query)).map(extensionMapper(downloads))
if (installed.isNotEmpty() || untrusted.isNotEmpty()) { if (installed.isNotEmpty() || untrusted.isNotEmpty()) {
items.add(ExtensionUiModel.Header.Resource(R.string.ext_installed)) items.add(ExtensionUiModel.Header.Resource(R.string.ext_installed))
items.addAll(installed) items.addAll(installed)
@ -110,21 +108,22 @@ class ExtensionsPresenter(
items items
}.collectLatest { }.collectLatest {
state.isRefreshing = false
state.isLoading = false state.isLoading = false
state.items = it state.items = it
} }
} }
presenterScope.launchIO { findAvailableExtensions() }
} }
fun search(query: String) { fun search(query: String) {
launchIO { presenterScope.launchIO {
_query.emit(query) _query.emit(query)
} }
} }
fun updateAllExtensions() { fun updateAllExtensions() {
launchIO { presenterScope.launchIO {
if (state.isEmpty) return@launchIO if (state.isEmpty) return@launchIO
val items = state.items val items = state.items
items.mapNotNull { items.mapNotNull {
@ -151,16 +150,16 @@ class ExtensionsPresenter(
} }
private fun removeDownloadState(extension: Extension) { private fun removeDownloadState(extension: Extension) {
_currentDownloads.update { map -> _currentDownloads.update { _map ->
val map = map.toMutableMap() val map = _map.toMutableMap()
map.remove(extension.pkgName) map.remove(extension.pkgName)
map map
} }
} }
private fun addDownloadState(extension: Extension, installStep: InstallStep) { private fun addDownloadState(extension: Extension, installStep: InstallStep) {
_currentDownloads.update { map -> _currentDownloads.update { _map ->
val map = map.toMutableMap() val map = _map.toMutableMap()
map[extension.pkgName] = installStep map[extension.pkgName] = installStep
map map
} }
@ -180,8 +179,11 @@ class ExtensionsPresenter(
} }
fun findAvailableExtensions() { fun findAvailableExtensions() {
state.isRefreshing = true presenterScope.launchIO {
extensionManager.findAvailableExtensions() state.isRefreshing = true
extensionManager.findAvailableExtensions()
state.isRefreshing = false
}
} }
fun trustSignature(signatureHash: String) { fun trustSignature(signatureHash: String) {