mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Add data listener to browse source
This commit is contained in:
@@ -13,6 +13,7 @@ import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
@@ -27,9 +28,10 @@ class ServerListeners @Inject constructor() {
|
||||
|
||||
private fun <T> Flow<T>.startWith(value: T) = onStart { emit(value) }
|
||||
|
||||
private val mangaListener = MutableSharedFlow<List<Long>>(
|
||||
private val _mangaListener = MutableSharedFlow<List<Long>>(
|
||||
extraBufferCapacity = Channel.UNLIMITED
|
||||
)
|
||||
val mangaListener = _mangaListener.asSharedFlow()
|
||||
|
||||
private val chapterIndexesListener = MutableSharedFlow<Pair<Long, List<Int>?>>(
|
||||
extraBufferCapacity = Channel.UNLIMITED
|
||||
@@ -49,18 +51,18 @@ class ServerListeners @Inject constructor() {
|
||||
|
||||
fun <T> combineMangaUpdates(flow: Flow<T>, predate: (suspend (List<Long>) -> Boolean)? = null) =
|
||||
if (predate != null) {
|
||||
mangaListener
|
||||
_mangaListener
|
||||
.filter(predate)
|
||||
.startWith(Unit)
|
||||
} else {
|
||||
mangaListener.startWith(Unit)
|
||||
_mangaListener.startWith(Unit)
|
||||
}
|
||||
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
.flatMapLatest { flow }
|
||||
|
||||
fun updateManga(vararg ids: Long) {
|
||||
scope.launch {
|
||||
mangaListener.emit(ids.toList())
|
||||
_mangaListener.emit(ids.toList())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.jui.domain.source.interactor
|
||||
|
||||
import ca.gosyer.jui.domain.ServerListeners
|
||||
import ca.gosyer.jui.domain.manga.interactor.GetManga
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.domain.source.model.MangaPage
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
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.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.runningFold
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import org.lighthousegames.logging.logging
|
||||
|
||||
class SourcePager @Inject constructor(
|
||||
private val getManga: GetManga,
|
||||
private val serverListeners: ServerListeners,
|
||||
private val fetcher: suspend (page: Int) -> MangaPage?,
|
||||
) : CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) {
|
||||
private val sourceMutex = Mutex()
|
||||
|
||||
private val _sourceManga = MutableStateFlow<List<Manga>>(emptyList())
|
||||
|
||||
private val mangaIds = _sourceManga.map { mangas -> mangas.map { it.id } }
|
||||
.stateIn(this, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
private val changedManga = serverListeners.mangaListener.runningFold(emptyMap<Long, Manga>()) { manga, updatedMangaIds ->
|
||||
coroutineScope {
|
||||
manga + updatedMangaIds.filter { it in mangaIds.value }.map {
|
||||
async {
|
||||
getManga.await(it)
|
||||
}
|
||||
}.awaitAll().filterNotNull().associateBy { it.id }
|
||||
}
|
||||
}.stateIn(this, SharingStarted.Eagerly, emptyMap())
|
||||
|
||||
val mangas = combine(_sourceManga, changedManga) { sourceManga, changedManga ->
|
||||
sourceManga.map { changedManga[it.id] ?: it }
|
||||
}.stateIn(this, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
private val _pageNum = MutableStateFlow(0)
|
||||
val pageNum = _pageNum.asStateFlow()
|
||||
|
||||
private val _hasNextPage = MutableStateFlow(true)
|
||||
val hasNextPage = _hasNextPage.asStateFlow()
|
||||
|
||||
private val _loading = MutableStateFlow(true)
|
||||
val loading = _loading.asStateFlow()
|
||||
|
||||
fun loadNextPage() {
|
||||
launch {
|
||||
if (hasNextPage.value && sourceMutex.tryLock()) {
|
||||
_pageNum.value++
|
||||
val page = fetcher(_pageNum.value)
|
||||
if (page != null) {
|
||||
_sourceManga.value = _sourceManga.value + page.mangaList
|
||||
_hasNextPage.value = page.hasNextPage
|
||||
} else {
|
||||
_pageNum.value--
|
||||
}
|
||||
sourceMutex.unlock()
|
||||
}
|
||||
_loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = logging()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user