ref: publish search catalogs as they arrive

This commit is contained in:
tapframe 2026-05-10 13:55:24 +05:30
parent 5cdda57913
commit 95708b9b79
2 changed files with 64 additions and 9 deletions

View file

@ -20,11 +20,11 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import nuvio.composeapp.generated.resources.* import nuvio.composeapp.generated.resources.*
import org.jetbrains.compose.resources.getString import org.jetbrains.compose.resources.getString
@ -91,16 +91,57 @@ object SearchRepository {
_uiState.value = SearchUiState(isLoading = true) _uiState.value = SearchUiState(isLoading = true)
activeJob = scope.launch { activeJob = scope.launch {
val results = requests.map { request -> val resultChannel = Channel<IndexedSearchResult>(Channel.UNLIMITED)
async { val jobs = requests.mapIndexed { index, request ->
launch {
runCatching { request.toSection() } runCatching { request.toSection() }
.fold(
onSuccess = { section ->
resultChannel.send(
IndexedSearchResult(
index = index,
section = section,
),
)
},
onFailure = { error ->
if (error is CancellationException) throw error
resultChannel.send(
IndexedSearchResult(
index = index,
error = error,
),
)
},
)
} }
}.awaitAll() }
val closeChannelJob = launch {
jobs.joinAll()
resultChannel.close()
}
val results = arrayOfNulls<IndexedSearchResult>(requests.size)
val sections = results try {
.mapNotNull { it.getOrNull() } for (result in resultChannel) {
val firstFailure = results.firstNotNullOfOrNull { it.exceptionOrNull()?.message } results[result.index] = result
val allFailed = results.isNotEmpty() && results.all { it.isFailure } val sections = results.orderedSections()
if (sections.isNotEmpty()) {
_uiState.value = SearchUiState(
isLoading = true,
sections = sections,
)
}
}
} finally {
closeChannelJob.cancel()
resultChannel.close()
}
val completedResults = results.filterNotNull()
val sections = results.orderedSections()
val firstFailure = completedResults.firstNotNullOfOrNull { it.error?.message }
val allFailed = completedResults.isNotEmpty() && completedResults.all { it.error != null }
_uiState.value = SearchUiState( _uiState.value = SearchUiState(
isLoading = false, isLoading = false,
@ -436,6 +477,15 @@ object SearchRepository {
} }
} }
private data class IndexedSearchResult(
val index: Int,
val section: HomeCatalogSection? = null,
val error: Throwable? = null,
)
private fun Array<IndexedSearchResult?>.orderedSections(): List<HomeCatalogSection> =
mapNotNull { result -> result?.section }
private fun CatalogPage.withUnreleasedFilter(): CatalogPage { private fun CatalogPage.withUnreleasedFilter(): CatalogPage {
if (!HomeCatalogSettingsRepository.snapshot().hideUnreleasedContent) return this if (!HomeCatalogSettingsRepository.snapshot().hideUnreleasedContent) return this
val filteredItems = items.filterReleasedItems(CurrentDateProvider.todayIsoDate()) val filteredItems = items.filterReleasedItems(CurrentDateProvider.todayIsoDate())

View file

@ -334,6 +334,11 @@ fun SearchScreen(
onPosterLongClick = onPosterLongClick, onPosterLongClick = onPosterLongClick,
) )
} }
if (uiState.isLoading) {
item(key = "search_loading_more") {
HomeSkeletonRow(modifier = Modifier.padding(horizontal = homeSectionPadding))
}
}
} }
} }
} }