From ebfc0f89ed9668bf68d43209e768b8d7882b5ee6 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 30 Oct 2022 18:43:16 -0400 Subject: [PATCH] Fix some crashes - Delay the initial emission of updates/sources/extensions lists instead of using a state flow. This hopefully avoids rapid initial recompositions that cause the LazyColumn key duplication crashes. (Closes #8371) - Fix a NPE in BrowseSourcePresenter (cherry picked from commit 5d1f79012e63403ac37309dd958244108e28a1a5) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt --- .../tachiyomi/ui/browse/extension/ExtensionsPresenter.kt | 5 +++-- .../eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt | 4 +++- .../ui/browse/source/browse/BrowseSourcePresenter.kt | 3 ++- .../kanade/tachiyomi/ui/recent/updates/UpdatesPresenter.kt | 5 +++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsPresenter.kt index a603be96c..da55bc14e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsPresenter.kt @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.system.LocaleHelper import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -22,7 +23,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.update import rx.Observable import uy.kohesive.injekt.Injekt @@ -116,7 +117,7 @@ class ExtensionsPresenter( items } - .stateIn(presenterScope) + .onStart { delay(500) } // Defer to avoid crashing on initial render .collectLatest { state.isLoading = false state.items = it diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt index cbbf9399c..0bfa48005 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt @@ -19,14 +19,15 @@ import eu.kanade.tachiyomi.util.system.logcat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.flow.stateIn import logcat.LogPriority import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -69,6 +70,7 @@ class SourcesPresenter( logcat(LogPriority.ERROR, exception) _events.send(Event.FailedFetchingSources) } + .onStart { delay(500) } // Defer to avoid crashing on initial render .flowOn(Dispatchers.IO) .launchIn(presenterScope) // SY <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index e494dde54..eea916abc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -209,7 +209,8 @@ open class BrowseSourcePresenter( // SY <-- fun reset() { - state.filters = source!!.getFilterList() + val source = source ?: return + state.filters = source.getFilterList() if (currentFilter !is Filter.UserInput) return state.currentFilter = (currentFilter as Filter.UserInput).copy(filters = state.filters) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesPresenter.kt index 410860a71..1c04e99f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/updates/UpdatesPresenter.kt @@ -29,13 +29,14 @@ import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.system.logcat import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import logcat.LogPriority import uy.kohesive.injekt.Injekt @@ -87,11 +88,11 @@ class UpdatesPresenter( getUpdates.subscribe(calendar).distinctUntilChanged(), downloadCache.changes, ) { updates, _ -> updates } + .onStart { delay(500) } // Defer to avoid crashing on initial render .catch { logcat(LogPriority.ERROR, it) _events.send(Event.InternalError) } - .stateIn(presenterScope) .collectLatest { updates -> state.items = updates.toUpdateItems() state.isLoading = false