Fix isLoading LiveData

This commit is contained in:
Koitharu
2022-05-12 13:44:05 +03:00
parent d9044b2d03
commit 317252e1dd
6 changed files with 49 additions and 25 deletions

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.base.ui package org.koitharu.kotatsu.base.ui
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@@ -11,8 +12,14 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
abstract class BaseViewModel : ViewModel() { abstract class BaseViewModel : ViewModel() {
val onError = SingleLiveEvent<Throwable>() protected val loadingCounter = CountedBooleanLiveData()
val isLoading = CountedBooleanLiveData() protected val errorEvent = SingleLiveEvent<Throwable>()
val onError: LiveData<Throwable>
get() = errorEvent
val isLoading: LiveData<Boolean>
get() = loadingCounter
protected fun launchJob( protected fun launchJob(
context: CoroutineContext = EmptyCoroutineContext, context: CoroutineContext = EmptyCoroutineContext,
@@ -25,18 +32,18 @@ abstract class BaseViewModel : ViewModel() {
start: CoroutineStart = CoroutineStart.DEFAULT, start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit block: suspend CoroutineScope.() -> Unit
): Job = viewModelScope.launch(context + createErrorHandler(), start) { ): Job = viewModelScope.launch(context + createErrorHandler(), start) {
isLoading.postValue(true) loadingCounter.increment()
try { try {
block() block()
} finally { } finally {
isLoading.postValue(false) loadingCounter.decrement()
} }
} }
private fun createErrorHandler() = CoroutineExceptionHandler { _, throwable -> private fun createErrorHandler() = CoroutineExceptionHandler { _, throwable ->
throwable.printStackTraceDebug() throwable.printStackTraceDebug()
if (throwable !is CancellationException) { if (throwable !is CancellationException) {
onError.postCall(throwable) errorEvent.postCall(throwable)
} }
} }
} }

View File

@@ -1,20 +1,31 @@
package org.koitharu.kotatsu.base.ui.util package org.koitharu.kotatsu.base.ui.util
import androidx.lifecycle.MutableLiveData import androidx.annotation.AnyThread
import androidx.lifecycle.LiveData
import java.util.concurrent.atomic.AtomicInteger
class CountedBooleanLiveData : MutableLiveData<Boolean>(false) { class CountedBooleanLiveData : LiveData<Boolean>(false) {
private var counter = 0 private val counter = AtomicInteger(0)
override fun setValue(value: Boolean) { @AnyThread
if (value) { fun increment() {
counter++ if (counter.getAndIncrement() == 0) {
} else { postValue(true)
counter--
} }
val newValue = counter > 0 }
if (newValue != this.value) {
super.setValue(newValue) @AnyThread
fun decrement() {
if (counter.decrementAndGet() == 0) {
postValue(false)
}
}
@AnyThread
fun reset() {
if (counter.getAndSet(0) != 0) {
postValue(false)
} }
} }
} }

View File

@@ -2,10 +2,13 @@ package org.koitharu.kotatsu.history.ui
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import java.util.*
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.domain.ReversibleHandle import org.koitharu.kotatsu.base.domain.ReversibleHandle
import org.koitharu.kotatsu.base.domain.plus import org.koitharu.kotatsu.base.domain.plus
@@ -23,8 +26,6 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.daysDiff import org.koitharu.kotatsu.utils.ext.daysDiff
import org.koitharu.kotatsu.utils.ext.onFirst import org.koitharu.kotatsu.utils.ext.onFirst
import java.util.*
import java.util.concurrent.TimeUnit
class HistoryListViewModel( class HistoryListViewModel(
private val repository: HistoryRepository, private val repository: HistoryRepository,
@@ -55,8 +56,10 @@ class HistoryListViewModel(
) )
else -> mapList(list, grouped, mode) else -> mapList(list, grouped, mode)
} }
}.onStart {
loadingCounter.increment()
}.onFirst { }.onFirst {
isLoading.postValue(false) loadingCounter.decrement()
}.catch { }.catch {
it.toErrorState(canRetry = false) it.toErrorState(canRetry = false)
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))

View File

@@ -136,7 +136,7 @@ class RemoteListViewModel(
e.printStackTraceDebug() e.printStackTraceDebug()
listError.value = e listError.value = e
if (!mangaList.value.isNullOrEmpty()) { if (!mangaList.value.isNullOrEmpty()) {
onError.postCall(e) errorEvent.postCall(e)
} }
} }
} }

View File

@@ -67,19 +67,19 @@ class GlobalSearchViewModel(
searchJob = repository.globalSearch(query) searchJob = repository.globalSearch(query)
.catch { e -> .catch { e ->
listError.value = e listError.value = e
isLoading.postValue(false) loadingCounter.reset()
}.onStart { }.onStart {
mangaList.value = null mangaList.value = null
listError.value = null listError.value = null
isLoading.postValue(true) loadingCounter.increment()
hasNextPage.value = true hasNextPage.value = true
}.onEmpty { }.onEmpty {
mangaList.value = emptyList() mangaList.value = emptyList()
}.onCompletion { }.onCompletion {
isLoading.postValue(false) loadingCounter.reset()
hasNextPage.value = false hasNextPage.value = false
}.onFirst { }.onFirst {
isLoading.postValue(false) loadingCounter.reset()
}.onEach { }.onEach {
mangaList.value = mangaList.value?.plus(it) ?: listOf(it) mangaList.value = mangaList.value?.plus(it) ?: listOf(it)
}.launchIn(viewModelScope + Dispatchers.Default) }.launchIn(viewModelScope + Dispatchers.Default)

View File

@@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onStart
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.MangaListViewModel
@@ -37,8 +38,10 @@ class SuggestionsViewModel(
list.toUi(this, mode) list.toUi(this, mode)
} }
} }
}.onStart {
loadingCounter.increment()
}.onFirst { }.onFirst {
isLoading.postValue(false) loadingCounter.decrement()
}.catch { }.catch {
it.toErrorState(canRetry = false) it.toErrorState(canRetry = false)
}.asLiveDataDistinct( }.asLiveDataDistinct(