Optimize the Downloaded quick filter
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user