Optimize the Downloaded quick filter

This commit is contained in:
Koitharu
2024-09-06 11:35:23 +03:00
parent 73cea59691
commit b601b07586
3 changed files with 31 additions and 23 deletions

View File

@@ -9,8 +9,10 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
@@ -55,7 +57,7 @@ class FavouritesListViewModel @Inject constructor(
val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID
private val refreshTrigger = MutableStateFlow(Any())
private val limit = MutableStateFlow(PAGE_SIZE)
private val isReady = AtomicBoolean(false)
private val isPaginationReady = AtomicBoolean(false)
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_FAVORITES) { favoritesListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode)
@@ -76,7 +78,9 @@ class FavouritesListViewModel @Inject constructor(
observeListModeWithTriggers(),
refreshTrigger,
) { list, filters, mode, _ ->
list.mapList(mode, filters).also { isReady.set(true) }
list.mapList(mode, filters)
}.distinctUntilChanged().onEach {
isPaginationReady.set(true)
}.catch {
emit(listOf(it.toErrorState(canRetry = false)))
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
@@ -118,7 +122,7 @@ class FavouritesListViewModel @Inject constructor(
}
fun requestMoreItems() {
if (isReady.compareAndSet(true, false)) {
if (isPaginationReady.compareAndSet(true, false)) {
limit.value += PAGE_SIZE
}
}
@@ -143,7 +147,7 @@ class FavouritesListViewModel @Inject constructor(
quickFilter.appliedOptions.combineWithSettings(),
limit,
) { order, filters, limit ->
isReady.set(false)
isPaginationReady.set(false)
repository.observeAll(order, filters, limit)
}.flattenLatest()
} else {

View File

@@ -8,7 +8,8 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
@@ -21,7 +22,6 @@ import org.koitharu.kotatsu.core.ui.util.ReversibleAction
import org.koitharu.kotatsu.core.util.ext.calculateTimeAgo
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.flattenLatest
import org.koitharu.kotatsu.core.util.ext.onFirst
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.history.domain.HistoryListQuickFilter
@@ -75,7 +75,7 @@ class HistoryListViewModel @Inject constructor(
}
private val limit = MutableStateFlow(PAGE_SIZE)
private val isReady = AtomicBoolean(false)
private val isPaginationReady = AtomicBoolean(false)
val isStatsEnabled = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
@@ -90,11 +90,9 @@ class HistoryListViewModel @Inject constructor(
observeListModeWithTriggers(),
settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
) { filters, list, grouped, mode, incognito ->
mapList(list, grouped, mode, filters, incognito).also { isReady.set(true) }
}.onStart {
loadingCounter.increment()
}.onFirst {
loadingCounter.decrement()
mapList(list, grouped, mode, filters, incognito)
}.distinctUntilChanged().onEach {
isPaginationReady.set(true)
}.catch { e ->
emit(listOf(e.toErrorState(canRetry = false)))
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
@@ -133,7 +131,7 @@ class HistoryListViewModel @Inject constructor(
}
fun requestMoreItems() {
if (isReady.compareAndSet(true, false)) {
if (isPaginationReady.compareAndSet(true, false)) {
limit.value += PAGE_SIZE
}
}
@@ -143,7 +141,7 @@ class HistoryListViewModel @Inject constructor(
quickFilter.appliedOptions.combineWithSettings(),
limit,
) { order, filters, limit ->
isReady.set(false)
isPaginationReady.set(false)
repository.observeAllWithHistory(order, filters, limit)
}.flattenLatest()

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.local.domain
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@@ -12,39 +13,44 @@ import kotlinx.coroutines.flow.transformLatest
import org.koitharu.kotatsu.core.model.isLocal
import org.koitharu.kotatsu.local.data.LocalMangaRepository
import org.koitharu.kotatsu.parsers.model.Manga
import java.util.Collections
import java.util.WeakHashMap
abstract class LocalObserveMapper<E, R>(
abstract class LocalObserveMapper<E : Any, R : Any>(
private val localMangaRepository: LocalMangaRepository,
private val limitStep: Int,
) {
private val cache = Collections.synchronizedMap(WeakHashMap<Manga, R?>())
protected fun observe(limit: Int, observer: (limit: Int) -> Flow<List<E>>): Flow<List<R>> {
val floatingLimit = MutableStateFlow(limit)
return floatingLimit.flatMapLatest { l ->
observer(l)
.transformLatest { fullList ->
val mapped = fullList.mapToLocal()
val mapped = fullList.mapToLocal(cache)
if (mapped.size < limit && fullList.size == l) {
floatingLimit.value += limitStep
} else {
emit(mapped.take(limit))
}
}.distinctUntilChanged()
}
}
private suspend fun List<E>.mapToLocal(): List<R> = coroutineScope {
val dispatcher = Dispatchers.IO.limitedParallelism(6)
map {
async(dispatcher) {
val m = toManga(it)
private suspend fun List<E>.mapToLocal(cache: MutableMap<Manga, R?>): List<R> = coroutineScope {
val dispatcher = Dispatchers.IO.limitedParallelism(8)
map { item ->
val m = toManga(item)
if (cache.contains(m)) {
CompletableDeferred(cache[m])
} else async(dispatcher) {
val mapped = if (m.isLocal) {
m
} else {
localMangaRepository.findSavedManga(m)?.manga
}
mapped?.let { mm -> toResult(it, mm) }
mapped?.let { mm -> toResult(item, mm) }.also { cache[m] = it }
}
}.awaitAll().filterNotNull()
}