|
|
|
|
@ -4,6 +4,9 @@ import androidx.lifecycle.SavedStateHandle
|
|
|
|
|
import androidx.lifecycle.viewModelScope
|
|
|
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
|
import kotlinx.coroutines.async
|
|
|
|
|
import kotlinx.coroutines.awaitAll
|
|
|
|
|
import kotlinx.coroutines.coroutineScope
|
|
|
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
|
|
|
import kotlinx.coroutines.flow.SharingStarted
|
|
|
|
|
import kotlinx.coroutines.flow.StateFlow
|
|
|
|
|
@ -15,21 +18,28 @@ import kotlinx.coroutines.flow.map
|
|
|
|
|
import kotlinx.coroutines.flow.stateIn
|
|
|
|
|
import kotlinx.coroutines.plus
|
|
|
|
|
import org.koitharu.kotatsu.R
|
|
|
|
|
import org.koitharu.kotatsu.core.model.isLocal
|
|
|
|
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
|
|
|
|
import org.koitharu.kotatsu.core.prefs.ListMode
|
|
|
|
|
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
|
|
|
|
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
|
|
|
|
import org.koitharu.kotatsu.core.util.ext.call
|
|
|
|
|
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
|
|
|
|
import org.koitharu.kotatsu.favourites.domain.FavoritesListQuickFilter
|
|
|
|
|
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
|
|
|
|
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.ARG_CATEGORY_ID
|
|
|
|
|
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
|
|
|
|
|
import org.koitharu.kotatsu.history.domain.MarkAsReadUseCase
|
|
|
|
|
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
|
|
|
|
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
|
|
|
|
import org.koitharu.kotatsu.list.domain.MangaListMapper
|
|
|
|
|
import org.koitharu.kotatsu.list.domain.QuickFilterListener
|
|
|
|
|
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
|
|
|
|
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.list.ui.model.toErrorState
|
|
|
|
|
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
|
|
|
|
import org.koitharu.kotatsu.parsers.model.Manga
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
|
|
|
import javax.inject.Inject
|
|
|
|
|
@ -42,9 +52,11 @@ class FavouritesListViewModel @Inject constructor(
|
|
|
|
|
private val repository: FavouritesRepository,
|
|
|
|
|
private val mangaListMapper: MangaListMapper,
|
|
|
|
|
private val markAsReadUseCase: MarkAsReadUseCase,
|
|
|
|
|
private val quickFilter: FavoritesListQuickFilter,
|
|
|
|
|
private val localMangaRepository: LocalMangaRepository,
|
|
|
|
|
settings: AppSettings,
|
|
|
|
|
downloadScheduler: DownloadWorker.Scheduler,
|
|
|
|
|
) : MangaListViewModel(settings, downloadScheduler) {
|
|
|
|
|
) : MangaListViewModel(settings, downloadScheduler), QuickFilterListener by quickFilter {
|
|
|
|
|
|
|
|
|
|
val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID
|
|
|
|
|
private val refreshTrigger = MutableStateFlow(Any())
|
|
|
|
|
@ -66,26 +78,19 @@ class FavouritesListViewModel @Inject constructor(
|
|
|
|
|
|
|
|
|
|
override val content = combine(
|
|
|
|
|
observeFavorites(),
|
|
|
|
|
quickFilter.appliedOptions,
|
|
|
|
|
observeListModeWithTriggers(),
|
|
|
|
|
refreshTrigger,
|
|
|
|
|
) { list, mode, _ ->
|
|
|
|
|
) { list, filters, mode, _ ->
|
|
|
|
|
when {
|
|
|
|
|
list.isEmpty() -> listOf(
|
|
|
|
|
EmptyState(
|
|
|
|
|
icon = R.drawable.ic_empty_favourites,
|
|
|
|
|
textPrimary = R.string.text_empty_holder_primary,
|
|
|
|
|
textSecondary = if (categoryId == NO_ID) {
|
|
|
|
|
R.string.you_have_not_favourites_yet
|
|
|
|
|
} else {
|
|
|
|
|
R.string.favourites_category_empty
|
|
|
|
|
},
|
|
|
|
|
actionStringRes = 0,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
list.isEmpty() -> if (filters.isEmpty()) {
|
|
|
|
|
listOf(getEmptyState(hasFilters = false))
|
|
|
|
|
} else {
|
|
|
|
|
listOf(quickFilter.filterItem(filters), getEmptyState(hasFilters = true))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
isReady.set(true)
|
|
|
|
|
mangaListMapper.toListModelList(list, mode)
|
|
|
|
|
list.mapList(mode, filters).also { isReady.set(true) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.catch {
|
|
|
|
|
@ -134,12 +139,55 @@ class FavouritesListViewModel @Inject constructor(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private suspend fun List<Manga>.mapList(mode: ListMode, filters: Set<ListFilterOption>): List<ListModel> {
|
|
|
|
|
val list = if (ListFilterOption.Downloaded in filters) {
|
|
|
|
|
mapToLocal()
|
|
|
|
|
} else {
|
|
|
|
|
this
|
|
|
|
|
}
|
|
|
|
|
val result = ArrayList<ListModel>(list.size + 1)
|
|
|
|
|
result += quickFilter.filterItem(filters)
|
|
|
|
|
mangaListMapper.toListModelList(result, list, mode)
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun observeFavorites() = if (categoryId == NO_ID) {
|
|
|
|
|
combine(sortOrder.filterNotNull(), limit, ::Pair)
|
|
|
|
|
.flatMapLatest { repository.observeAll(it.first, it.second) }
|
|
|
|
|
combine(sortOrder.filterNotNull(), quickFilter.appliedOptions, limit, ::Triple)
|
|
|
|
|
.flatMapLatest { repository.observeAll(it.first, it.second - ListFilterOption.Downloaded, it.third) }
|
|
|
|
|
} else {
|
|
|
|
|
limit.flatMapLatest {
|
|
|
|
|
repository.observeAll(categoryId, it)
|
|
|
|
|
}
|
|
|
|
|
combine(quickFilter.appliedOptions, limit, ::Pair)
|
|
|
|
|
.flatMapLatest { repository.observeAll(categoryId, it.first - ListFilterOption.Downloaded, it.second) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun getEmptyState(hasFilters: Boolean) = if (hasFilters) {
|
|
|
|
|
EmptyState(
|
|
|
|
|
icon = R.drawable.ic_empty_favourites,
|
|
|
|
|
textPrimary = R.string.nothing_found,
|
|
|
|
|
textSecondary = R.string.text_empty_holder_secondary_filtered,
|
|
|
|
|
actionStringRes = R.string.reset_filter,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
EmptyState(
|
|
|
|
|
icon = R.drawable.ic_empty_favourites,
|
|
|
|
|
textPrimary = R.string.text_empty_holder_primary,
|
|
|
|
|
textSecondary = if (categoryId == NO_ID) {
|
|
|
|
|
R.string.you_have_not_favourites_yet
|
|
|
|
|
} else {
|
|
|
|
|
R.string.favourites_category_empty
|
|
|
|
|
},
|
|
|
|
|
actionStringRes = 0,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private suspend fun List<Manga>.mapToLocal(): List<Manga> = coroutineScope {
|
|
|
|
|
map {
|
|
|
|
|
async {
|
|
|
|
|
if (it.isLocal) {
|
|
|
|
|
it
|
|
|
|
|
} else {
|
|
|
|
|
localMangaRepository.findSavedManga(it)?.manga
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.awaitAll().filterNotNull()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|