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