Handle filter loading errors

This commit is contained in:
Koitharu
2022-03-04 08:16:36 +02:00
parent 5c05aaeacf
commit 3c64d6675e
7 changed files with 49 additions and 13 deletions

View File

@@ -10,4 +10,5 @@ class FilterAdapter(
filterTagDelegate(listener),
filterHeaderDelegate(),
filterLoadingDelegate(),
filterErrorDelegate(),
)

View File

@@ -1,10 +1,12 @@
package org.koitharu.kotatsu.list.ui.filter
import android.widget.TextView
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.ItemCheckableMultipleBinding
import org.koitharu.kotatsu.databinding.ItemCheckableSingleBinding
import org.koitharu.kotatsu.databinding.ItemFilterHeaderBinding
import org.koitharu.kotatsu.databinding.ItemLoadingFooterBinding
fun filterSortDelegate(
listener: OnFilterChangedListener,
@@ -47,6 +49,11 @@ fun filterHeaderDelegate() = adapterDelegateViewBinding<FilterItem.Header, Filte
}
}
fun filterLoadingDelegate() = adapterDelegateViewBinding<FilterItem.Loading, FilterItem, ItemLoadingFooterBinding>(
{ layoutInflater, parent -> ItemLoadingFooterBinding.inflate(layoutInflater, parent, false) }
) { }
fun filterLoadingDelegate() = adapterDelegate<FilterItem.Loading, FilterItem>(R.layout.item_loading_footer) {}
fun filterErrorDelegate() = adapterDelegate<FilterItem.Error, FilterItem>(R.layout.item_sources_empty) {
bind {
(itemView as TextView).setText(item.textResId)
}
}

View File

@@ -17,14 +17,18 @@ class FilterDiffCallback : DiffUtil.ItemCallback<FilterItem>() {
oldItem is FilterItem.Sort && newItem is FilterItem.Sort -> {
oldItem.order == newItem.order
}
oldItem is FilterItem.Error && newItem is FilterItem.Error -> {
oldItem.textResId == newItem.textResId
}
else -> false
}
}
override fun areContentsTheSame(oldItem: FilterItem, newItem: FilterItem): Boolean {
return when {
oldItem === newItem -> true
oldItem == FilterItem.Loading && newItem == FilterItem.Loading -> true
oldItem is FilterItem.Header && newItem is FilterItem.Header -> true
oldItem is FilterItem.Error && newItem is FilterItem.Error -> true
oldItem is FilterItem.Tag && newItem is FilterItem.Tag -> {
oldItem.isChecked == newItem.isChecked
}

View File

@@ -21,4 +21,8 @@ sealed interface FilterItem {
) : FilterItem
object Loading : FilterItem
class Error(
@StringRes val textResId: Int,
) : FilterItem
}

View File

@@ -6,6 +6,7 @@ import kotlinx.coroutines.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.domain.MangaDataRepository
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.model.SortOrder
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import java.util.*
@@ -21,12 +22,10 @@ class FilterViewModel(
private var job: Job? = null
private var selectedSortOrder: SortOrder? = state.sortOrder
private val selectedTags = HashSet(state.tags)
private val availableTagsDeferred = viewModelScope.async(Dispatchers.Default + createErrorHandler()) {
repository.getTags()
}
private val localTagsDeferred = viewModelScope.async(Dispatchers.Default + createErrorHandler()) {
private val localTagsDeferred = viewModelScope.async(Dispatchers.Default) {
dataRepository.findTags(repository.source)
}
private var availableTagsDeferred = loadTagsAsync()
init {
showFilter()
@@ -52,21 +51,24 @@ class FilterViewModel(
val previousJob = job
job = launchJob(Dispatchers.Default) {
previousJob?.cancelAndJoin()
val tags = availableTagsDeferred.await()
val tags = tryLoadTags()
val localTags = localTagsDeferred.await()
val sortOrders = repository.sortOrders
val list = ArrayList<FilterItem>(sortOrders.size + tags.size + 2)
val list = ArrayList<FilterItem>(sortOrders.size + (tags?.size ?: 1) + 2)
list.add(FilterItem.Header(R.string.sort_order))
sortOrders.sortedBy { it.ordinal }.mapTo(list) {
FilterItem.Sort(it, isSelected = it == selectedSortOrder)
}
if (tags.isNotEmpty() || selectedTags.isNotEmpty()) {
if (tags == null || tags.isNotEmpty() || selectedTags.isNotEmpty()) {
list.add(FilterItem.Header(R.string.genres))
val mappedTags = TreeSet<FilterItem.Tag>(compareBy({ !it.isChecked }, { it.tag.title }))
localTags.mapTo(mappedTags) { FilterItem.Tag(it, isChecked = it in selectedTags) }
tags.mapTo(mappedTags) { FilterItem.Tag(it, isChecked = it in selectedTags) }
tags?.mapTo(mappedTags) { FilterItem.Tag(it, isChecked = it in selectedTags) }
selectedTags.mapTo(mappedTags) { FilterItem.Tag(it, isChecked = true) }
list.addAll(mappedTags)
if (tags == null) {
list.add(FilterItem.Error(R.string.filter_load_error))
}
}
ensureActive()
filter.postValue(list)
@@ -93,4 +95,20 @@ class FilterViewModel(
updateFilters()
}
}
private suspend fun tryLoadTags(): Set<MangaTag>? {
val shouldRetryOnError = availableTagsDeferred.isCompleted
val result = availableTagsDeferred.await()
if (result == null && shouldRetryOnError) {
availableTagsDeferred = loadTagsAsync()
return availableTagsDeferred.await()
}
return result
}
private fun loadTagsAsync() = viewModelScope.async(Dispatchers.Default) {
kotlin.runCatching {
repository.getTags()
}.getOrNull()
}
}

View File

@@ -253,4 +253,5 @@
<string name="screenshots_allow">Разрешить</string>
<string name="screenshots_block_nsfw">Запретить для NSFW</string>
<string name="screenshots_block_all">Запретить всегда</string>
<string name="filter_load_error">Не удалось загрузить список жанров</string>
</resources>

View File

@@ -255,4 +255,5 @@
<string name="screenshots_allow">Allow</string>
<string name="screenshots_block_nsfw">Block on NSFW</string>
<string name="screenshots_block_all">Block always</string>
<string name="filter_load_error">Unable to load genres list</string>
</resources>