Browse Source

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
arkon 2 years ago
parent
commit
5d1f79012e

+ 3 - 2
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

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

@@ -14,10 +14,11 @@ import eu.kanade.tachiyomi.util.lang.launchIO
 import eu.kanade.tachiyomi.util.system.logcat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.collectLatest
+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
@@ -43,7 +44,7 @@ class SourcesPresenter(
                     logcat(LogPriority.ERROR, exception)
                     _events.send(Event.FailedFetchingSources)
                 }
-                .stateIn(presenterScope)
+                .onStart { delay(500) } // Defer to avoid crashing on initial render
                 .collectLatest(::collectLatestSources)
         }
     }

+ 2 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt

@@ -154,7 +154,8 @@ open class BrowseSourcePresenter(
     }
 
     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)
     }

+ 3 - 2
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