Fix isLoading LiveData

pull/168/head
Koitharu 4 years ago
parent d9044b2d03
commit 317252e1dd
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -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)
} }
} }
} }

@ -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)
} }
} }
} }

@ -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))

@ -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)
} }
} }
} }

@ -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)

@ -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(

Loading…
Cancel
Save