@ -10,22 +10,27 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
import org.koitharu.kotatsu.filter.ui.model.TagCatalogItem
import org.koitharu.kotatsu.filter.ui.model.TagCatalogItem
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorFooter
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.MangaTag
@HiltViewModel ( assistedFactory = TagsCatalogViewModel . Factory :: class )
@HiltViewModel ( assistedFactory = TagsCatalogViewModel . Factory :: class )
class TagsCatalogViewModel @AssistedInject constructor (
class TagsCatalogViewModel @AssistedInject constructor (
@Assisted private val filter : FilterCoordinator ,
@Assisted private val filter : FilterCoordinator ,
@Assisted private val isExcluded : Boolean ,
@Assisted private val isExcluded : Boolean ,
private val mangaDataRepository : MangaDataRepository ,
) : BaseViewModel ( ) {
) : BaseViewModel ( ) {
val searchQuery = MutableStateFlow ( " " )
val searchQuery = MutableStateFlow ( " " )
@ -33,23 +38,13 @@ class TagsCatalogViewModel @AssistedInject constructor(
private val filterProperty : StateFlow < FilterProperty < MangaTag > >
private val filterProperty : StateFlow < FilterProperty < MangaTag > >
get ( ) = if ( isExcluded ) filter . tagsExcluded else filter . tags
get ( ) = if ( isExcluded ) filter . tagsExcluded else filter . tags
@Suppress ( " RemoveExplicitTypeArguments " )
private val tags : StateFlow < List < ListModel > > = combine (
private val tags : StateFlow < List < ListModel > > = combine (
filter . getAllTags ( ) ,
filter . getAllTags ( ) ,
flow < Collection < MangaTag > > { emit ( emptyList ( ) ) ; emit ( mangaDataRepository . findTags ( filter . mangaSource ) ) } ,
filterProperty . map { it . selectedItems } ,
filterProperty . map { it . selectedItems } ,
) { all , selected ->
) { available , cached , selected ->
all . fold (
buildList ( available , cached , selected )
onSuccess = {
it . map { tag ->
TagCatalogItem (
tag = tag ,
isChecked = tag in selected ,
)
}
} ,
onFailure = {
listOf ( it . toErrorState ( false ) )
} ,
)
} . stateIn ( viewModelScope + Dispatchers . Default , SharingStarted . Eagerly , listOf ( LoadingState ) )
} . stateIn ( viewModelScope + Dispatchers . Default , SharingStarted . Eagerly , listOf ( LoadingState ) )
val content = combine ( tags , searchQuery ) { raw , query ->
val content = combine ( tags , searchQuery ) { raw , query ->
@ -66,6 +61,50 @@ class TagsCatalogViewModel @AssistedInject constructor(
}
}
}
}
private fun buildList (
available : Result < List < MangaTag > > ,
cached : Collection < MangaTag > ,
selected : Set < MangaTag > ,
) : List < ListModel > {
val capacity = ( available . getOrNull ( ) ?. size ?: 1 ) + cached . size
val result = ArrayList < ListModel > ( capacity )
val added = HashSet < String > ( capacity )
available . getOrNull ( ) ?. forEach { tag ->
if ( added . add ( tag . title ) ) {
result . add (
TagCatalogItem (
tag = tag ,
isChecked = tag in selected ,
) ,
)
}
}
cached . forEach { tag ->
if ( added . add ( tag . title ) ) {
result . add (
TagCatalogItem (
tag = tag ,
isChecked = tag in selected ,
) ,
)
}
}
if ( result . isNotEmpty ( ) ) {
val locale = ( filter . mangaSource as ? MangaParserSource ) ?. locale
result . sortWith ( compareBy ( TagTitleComparator ( locale ) ) { ( it as TagCatalogItem ) . tag } )
}
available . exceptionOrNull ( ) ?. let { error ->
result . add (
if ( result . isEmpty ( ) ) {
error . toErrorState ( canRetry = false )
} else {
error . toErrorFooter ( )
} ,
)
}
return result
}
@AssistedFactory
@AssistedFactory
interface Factory {
interface Factory {
fun create ( filter : FilterCoordinator , isExcludeTag : Boolean ) : TagsCatalogViewModel
fun create ( filter : FilterCoordinator , isExcludeTag : Boolean ) : TagsCatalogViewModel