Add data listener to browse source

This commit is contained in:
Syer10
2023-01-15 22:09:43 -05:00
parent e203e13141
commit 7d9cf842d7
3 changed files with 154 additions and 65 deletions

View File

@@ -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())
}
}

View File

@@ -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()
}
}