Show errors in global search results

This commit is contained in:
Koitharu
2023-06-05 09:34:56 +03:00
parent 1847759ec3
commit c2ee548f0a
5 changed files with 58 additions and 38 deletions

View File

@@ -8,6 +8,7 @@ class MultiSearchListModel(
val source: MangaSource,
val hasMore: Boolean,
val list: List<MangaItemModel>,
val error: Throwable?,
) : ListModel {
override fun equals(other: Any?): Boolean {
@@ -19,14 +20,14 @@ class MultiSearchListModel(
if (source != other.source) return false
if (hasMore != other.hasMore) return false
if (list != other.list) return false
return true
return error == other.error
}
override fun hashCode(): Int {
var result = source.hashCode()
result = 31 * result + hasMore.hashCode()
result = 31 * result + list.hashCode()
result = 31 * result + (error?.hashCode() ?: 0)
return result
}
}
}

View File

@@ -16,8 +16,8 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.plus
import kotlinx.coroutines.withTimeout
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.CompositeException
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
@@ -30,7 +30,6 @@ import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
@@ -52,20 +51,17 @@ class MultiSearchViewModel @Inject constructor(
private var searchJob: Job? = null
private val listData = MutableStateFlow<List<MultiSearchListModel>>(emptyList())
private val loadingData = MutableStateFlow(false)
private var listError = MutableStateFlow<Throwable?>(null)
val onDownloadStarted = MutableEventFlow<Unit>()
val query = MutableStateFlow(savedStateHandle.get<String>(MultiSearchActivity.EXTRA_QUERY).orEmpty())
val list: StateFlow<List<ListModel>> = combine(
listData,
loadingData,
listError,
) { list, loading, error ->
) { list, loading ->
when {
list.isEmpty() -> listOf(
when {
loading -> LoadingState
error != null -> error.toErrorState(canRetry = true)
else -> EmptyState(
icon = R.drawable.ic_empty_common,
textPrimary = R.string.nothing_found,
@@ -101,15 +97,12 @@ class MultiSearchViewModel @Inject constructor(
searchJob = launchJob(Dispatchers.Default) {
prevJob?.cancelAndJoin()
try {
listError.value = null
listData.value = emptyList()
loadingData.value = true
query.value = q
searchImpl(q)
} catch (e: CancellationException) {
throw e
} catch (e: Throwable) {
listError.value = e
} finally {
loadingData.value = false
}
@@ -129,35 +122,28 @@ class MultiSearchViewModel @Inject constructor(
val deferredList = sources.map { source ->
async(dispatcher) {
runCatchingCancellable {
val list = mangaRepositoryFactory.create(source).getList(offset = 0, query = q)
.toUi(ListMode.GRID, extraProvider)
if (list.isNotEmpty()) {
MultiSearchListModel(source, list.size > MIN_HAS_MORE_ITEMS, list)
} else {
null
withTimeout(8_000) {
mangaRepositoryFactory.create(source).getList(offset = 0, query = q)
.toUi(ListMode.GRID, extraProvider)
}
}.onFailure {
it.printStackTraceDebug()
}
}.fold(
onSuccess = { list ->
if (list.isEmpty()) {
null
} else {
MultiSearchListModel(source, list.size > MIN_HAS_MORE_ITEMS, list, null)
}
},
onFailure = { error ->
error.printStackTraceDebug()
MultiSearchListModel(source, true, emptyList(), error)
},
)
}
}
val errors = ArrayList<Throwable>()
for (deferred in deferredList) {
deferred.await()
.onSuccess { item ->
if (item != null) {
listData.update { x -> x + item }
}
}.onFailure {
errors.add(it)
}
}
if (listData.value.isEmpty()) {
when (errors.size) {
0 -> Unit
1 -> throw errors[0]
else -> throw CompositeException(errors)
deferred.await()?.let { item ->
listData.update { x -> x + item }
}
}
}

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.search.ui.multi.adapter
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
@@ -10,6 +11,8 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ItemListGroupBinding
import org.koitharu.kotatsu.list.ui.ItemSizeResolver
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
@@ -46,5 +49,7 @@ fun searchResultsAD(
binding.buttonMore.isVisible = item.hasMore
adapter.notifyDataSetChanged()
adapter.items = item.list
binding.recyclerView.isGone = item.list.isEmpty()
binding.textViewError.textAndVisible = item.error?.getDisplayMessage(context.resources)
}
}

View File

@@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/error"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M11 15h2v2h-2zm0-8h2v6h-2zm0.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />
</vector>

View File

@@ -47,4 +47,21 @@
android:paddingHorizontal="@dimen/grid_spacing"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</RelativeLayout>
<TextView
android:id="@+id/textView_error"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/recyclerView"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_marginHorizontal="@dimen/grid_spacing"
android:drawablePadding="12dp"
android:gravity="center_vertical"
android:padding="@dimen/grid_spacing"
android:textAppearance="?attr/textAppearanceBodySmall"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_error_small"
tools:text="@tools:sample/lorem[6]"
tools:visibility="visible" />
</RelativeLayout>