@ -1,5 +1,7 @@
package org.koitharu.kotatsu.favourites.ui.categories.select
package org.koitharu.kotatsu.favourites.ui.categories.select
import androidx.collection.MutableLongObjectMap
import androidx.collection.MutableLongSet
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
@ -7,19 +9,20 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.ids
import org.koitharu.kotatsu.core.model.ids
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsFlow
import org.koitharu.kotatsu.core.prefs.observeAsFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.firstNotNull
import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.domain.model.Cover
import org.koitharu.kotatsu.favourites.ui.categories.select.model.CategoriesHeaderItem
import org.koitharu.kotatsu.favourites.ui.categories.select.model.CategoriesHeaderItem
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.mapToSet
import javax.inject.Inject
import javax.inject.Inject
@ -33,41 +36,52 @@ class FavoriteSheetViewModel @Inject constructor(
private val manga = savedStateHandle . require < List < ParcelableManga > > ( FavoriteSheet . KEY _MANGA _LIST ) . mapToSet {
private val manga = savedStateHandle . require < List < ParcelableManga > > ( FavoriteSheet . KEY _MANGA _LIST ) . mapToSet {
it . manga
it . manga
}
}
private val header = CategoriesHeaderItem ( )
private val header = CategoriesHeaderItem (
private val checkedCategories = MutableStateFlow < Set < Long > ? > ( null )
titles = manga . map { it . title } ,
covers = manga . take ( 3 ) . map {
Cover (
url = it . coverUrl ,
source = it . source . name ,
)
} ,
)
private val refreshTrigger = MutableStateFlow ( Any ( ) )
val content = combine (
val content = combine (
favouritesRepository . observeCategories ( ) ,
favouritesRepository . observeCategories ( ) ,
checkedCategories . filterNotNull ( ) ,
refreshTrigger ,
settings . observeAsFlow ( AppSettings . KEY _TRACKER _ENABLED ) { isTrackerEnabled } ,
settings . observeAsFlow ( AppSettings . KEY _TRACKER _ENABLED ) { isTrackerEnabled } ,
) { categories , checked , tracker ->
) { categories , _ , tracker ->
buildList ( categories . size + 1 ) {
mapList ( categories , tracker )
add ( header )
categories . mapTo ( this ) { cat ->
MangaCategoryItem (
category = cat ,
isChecked = cat . id in checked ,
isTrackerEnabled = tracker ,
)
}
}
} . stateIn ( viewModelScope + Dispatchers . Default , SharingStarted . Eagerly , listOf ( header ) )
} . stateIn ( viewModelScope + Dispatchers . Default , SharingStarted . Eagerly , listOf ( header ) )
init {
launchJob ( Dispatchers . Default ) {
checkedCategories . value = favouritesRepository . getCategoriesIds ( manga . ids ( ) )
}
}
fun setChecked ( categoryId : Long , isChecked : Boolean ) {
fun setChecked ( categoryId : Long , isChecked : Boolean ) {
launchJob ( Dispatchers . Default ) {
launchJob ( Dispatchers . Default ) {
val checkedIds = checkedCategories . firstNotNull ( )
if ( isChecked ) {
if ( isChecked ) {
checkedCategories . value = checkedIds + categoryId
favouritesRepository . addToCategory ( categoryId , manga )
favouritesRepository . addToCategory ( categoryId , manga )
} else {
} else {
checkedCategories . value = checkedIds - categoryId
favouritesRepository . removeFromCategory ( categoryId , manga . ids ( ) )
favouritesRepository . removeFromCategory ( categoryId , manga . ids ( ) )
}
}
refreshTrigger . value = Any ( )
}
}
private suspend fun mapList ( categories : List < FavouriteCategory > , tracker : Boolean ) : List < ListModel > {
val cats = MutableLongObjectMap < MutableLongSet > ( categories . size )
categories . forEach { cats [ it . id ] = MutableLongSet ( manga . size ) }
for ( m in manga ) {
val ids = favouritesRepository . getCategoriesIds ( m . id )
ids . forEach { id -> cats [ id ] ?. add ( m . id ) }
}
return buildList ( categories . size + 1 ) {
add ( header )
categories . mapTo ( this ) { cat ->
MangaCategoryItem (
category = cat ,
isChecked = cats [ cat . id ] ?. isNotEmpty ( ) == true ,
isTrackerEnabled = tracker ,
isEnabled = cats [ cat . id ] ?. let { it . size == 0 || it . size == manga . size } == true ,
)
}
}
}
}
}
}
}