diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt index 3931f89eb..62fa74862 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt @@ -67,7 +67,7 @@ class FavouriteCategoriesActivity : attachToRecyclerView(viewBinding.recyclerView) } - viewModel.categories.observe(this, ::onCategoriesChanged) + viewModel.content.observe(this, ::onCategoriesChanged) viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.recyclerView, null)) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt index b49dace7c..786ba5138 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt @@ -1,21 +1,20 @@ package org.koitharu.kotatsu.favourites.ui.categories -import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.plus +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collectLatest import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.requireValue import org.koitharu.kotatsu.favourites.domain.FavouritesRepository +import org.koitharu.kotatsu.favourites.domain.model.Cover import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel import org.koitharu.kotatsu.list.ui.model.EmptyState +import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.parsers.util.move import javax.inject.Inject @@ -28,26 +27,17 @@ class FavouritesCategoriesViewModel @Inject constructor( private var reorderJob: Job? = null - val categories = repository.observeCategoriesWithCovers() - .map { list -> - list.map { (category, covers) -> - CategoryListModel( - mangaCount = covers.size, - covers = covers.take(3), - category = category, - isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources, - ) - }.ifEmpty { - listOf( - EmptyState( - icon = R.drawable.ic_empty_favourites, - textPrimary = R.string.text_empty_holder_primary, - textSecondary = R.string.empty_favourite_categories, - actionStringRes = 0, - ), - ) - } - }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) + val content = MutableStateFlow>(listOf(LoadingState)) + + init { + launchJob(Dispatchers.Default) { + repository.observeCategoriesWithCovers() + .collectLatest { + reorderJob?.join() + updateContent(it) + } + } + } fun deleteCategories(ids: Set) { launchJob(Dispatchers.Default) { @@ -59,14 +49,15 @@ class FavouritesCategoriesViewModel @Inject constructor( settings.isAllFavouritesVisible = isVisible } - fun isEmpty(): Boolean = categories.value.none { it is CategoryListModel } + fun isEmpty(): Boolean = content.value.none { it is CategoryListModel } fun reorderCategories(oldPos: Int, newPos: Int) { val prevJob = reorderJob reorderJob = launchJob(Dispatchers.Default) { prevJob?.join() - val snapshot = categories.requireValue().toMutableList() + val snapshot = content.requireValue().toMutableList() snapshot.move(oldPos, newPos) + content.value = snapshot val ids = snapshot.mapNotNullTo(ArrayList(snapshot.size)) { (it as? CategoryListModel)?.category?.id } @@ -83,9 +74,29 @@ class FavouritesCategoriesViewModel @Inject constructor( } fun getCategories(ids: Set): ArrayList { - val items = categories.requireValue() + val items = content.requireValue() return items.mapNotNullTo(ArrayList(ids.size)) { item -> (item as? CategoryListModel)?.category?.takeIf { it.id in ids } } } + + private fun updateContent(categories: Map>) { + content.value = categories.map { (category, covers) -> + CategoryListModel( + mangaCount = covers.size, + covers = covers.take(3), + category = category, + isTrackerEnabled = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources, + ) + }.ifEmpty { + listOf( + EmptyState( + icon = R.drawable.ic_empty_favourites, + textPrimary = R.string.text_empty_holder_primary, + textSecondary = R.string.empty_favourite_categories, + actionStringRes = 0, + ), + ) + } + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageViewModel.kt index 4b2b389d7..1101168b3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageViewModel.kt @@ -2,17 +2,12 @@ package org.koitharu.kotatsu.settings.sources import androidx.annotation.CheckResult import androidx.core.os.LocaleListCompat -import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.cancelAndJoin -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.plus import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.getLocaleTitle import org.koitharu.kotatsu.core.prefs.AppSettings @@ -46,32 +41,39 @@ class SourcesManageViewModel @Inject constructor( private val expandedGroups = MutableStateFlow(emptySet()) private var searchQuery = MutableStateFlow(null) private var reorderJob: Job? = null - - val content = combine( - repository.observeEnabledSources(), - expandedGroups, - searchQuery, - observeTip(), - settings.observeAsFlow(AppSettings.KEY_DISABLE_NSFW) { isNsfwContentDisabled }, - ) { sources, groups, query, tip, noNsfw -> - buildList(sources, groups, query, tip, noNsfw) - }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList()) - + val content = MutableStateFlow>(emptyList()) val onActionDone = MutableEventFlow() + init { + launchJob(Dispatchers.Default) { + combine( + repository.observeEnabledSources(), + expandedGroups, + searchQuery, + observeTip(), + settings.observeAsFlow(AppSettings.KEY_DISABLE_NSFW) { isNsfwContentDisabled }, + ) { sources, groups, query, tip, noNsfw -> + buildList(sources, groups, query, tip, noNsfw) + }.collectLatest { + reorderJob?.join() + content.value = it + } + } + } + fun reorderSources(oldPos: Int, newPos: Int) { val snapshot = content.value.toMutableList() val prevJob = reorderJob reorderJob = launchJob(Dispatchers.Default) { - prevJob?.cancelAndJoin() if ((snapshot[oldPos] as? SourceConfigItem.SourceItem)?.isDraggable != true) { return@launchJob } if ((snapshot[newPos] as? SourceConfigItem.SourceItem)?.isDraggable != true) { return@launchJob } - delay(100) snapshot.move(oldPos, newPos) + content.value = snapshot + prevJob?.join() val newSourcesList = snapshot.mapNotNull { x -> if (x is SourceConfigItem.SourceItem && x.isDraggable) { x.source