mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Improve listener system
This commit is contained in:
@@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.buffer
|
import kotlinx.coroutines.flow.buffer
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
@@ -34,16 +33,6 @@ class ServerListeners {
|
|||||||
)
|
)
|
||||||
val mangaListener = _mangaListener.asSharedFlow()
|
val mangaListener = _mangaListener.asSharedFlow()
|
||||||
|
|
||||||
private val _chapterIdsListener = MutableSharedFlow<List<Long>>(
|
|
||||||
extraBufferCapacity = Channel.UNLIMITED,
|
|
||||||
)
|
|
||||||
val chapterIdsListener = _chapterIdsListener.asSharedFlow()
|
|
||||||
|
|
||||||
private val _mangaChapterIdsListener = MutableSharedFlow<List<Long>>(
|
|
||||||
extraBufferCapacity = Channel.UNLIMITED,
|
|
||||||
)
|
|
||||||
val mangaChapterIdsListener = _mangaChapterIdsListener.asSharedFlow()
|
|
||||||
|
|
||||||
private val categoryMangaListener = MutableSharedFlow<Long>(
|
private val categoryMangaListener = MutableSharedFlow<Long>(
|
||||||
extraBufferCapacity = Channel.UNLIMITED,
|
extraBufferCapacity = Channel.UNLIMITED,
|
||||||
)
|
)
|
||||||
@@ -65,9 +54,23 @@ class ServerListeners {
|
|||||||
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||||
.flatMapLatest { flow }
|
.flatMapLatest { flow }
|
||||||
|
|
||||||
fun updateManga(vararg ids: Long) {
|
fun updateManga(ids: List<Long>) {
|
||||||
|
val ids = ids.filter { id -> id >= 0 }
|
||||||
|
if (ids.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
scope.launch {
|
scope.launch {
|
||||||
_mangaListener.emit(ids.toList())
|
_mangaListener.emit(ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateManga(vararg ids: Long) {
|
||||||
|
val ids = ids.filter { id -> id >= 0 }
|
||||||
|
if (ids.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
scope.launch {
|
||||||
|
_mangaListener.emit(ids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,36 +91,6 @@ class ServerListeners {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> combineChapters(
|
|
||||||
flow: Flow<T>,
|
|
||||||
chapterIdPredate: (suspend (List<Long>) -> Boolean)? = null,
|
|
||||||
mangaIdPredate: (suspend (List<Long>) -> Boolean)? = null,
|
|
||||||
): Flow<T> {
|
|
||||||
val idsListener = _chapterIdsListener
|
|
||||||
.filter { chapterIdPredate?.invoke(it) ?: false }
|
|
||||||
.startWith(Unit)
|
|
||||||
.combine(
|
|
||||||
_mangaChapterIdsListener.filter { mangaIdPredate?.invoke(it) ?: false }
|
|
||||||
.startWith(Unit),
|
|
||||||
) { _, _ -> }
|
|
||||||
|
|
||||||
return idsListener
|
|
||||||
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
|
||||||
.flatMapLatest { flow }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateChapters(chapterIds: List<Long>) {
|
|
||||||
scope.launch {
|
|
||||||
_chapterIdsListener.emit(chapterIds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateChapters(vararg chapterIds: Long) {
|
|
||||||
scope.launch {
|
|
||||||
_chapterIdsListener.emit(chapterIds.toList())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ class DeleteChapterDownload(
|
|||||||
) {
|
) {
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
onError: suspend (Throwable) -> Unit = {},
|
onError: suspend (Throwable) -> Unit = {},
|
||||||
) = asFlow(chapterId)
|
) = asFlow(chapterId, mangaId)
|
||||||
.catch {
|
.catch {
|
||||||
onError(it)
|
onError(it)
|
||||||
log.warn(it) { "Failed to delete chapter download for $chapterId" }
|
log.warn(it) { "Failed to delete chapter download for $chapterId" }
|
||||||
@@ -44,8 +45,9 @@ class DeleteChapterDownload(
|
|||||||
|
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
chapterIds: List<Long>,
|
chapterIds: List<Long>,
|
||||||
|
mangaIds: List<Long>?,
|
||||||
onError: suspend (Throwable) -> Unit = {},
|
onError: suspend (Throwable) -> Unit = {},
|
||||||
) = asFlow(chapterIds)
|
) = asFlow(chapterIds, mangaIds)
|
||||||
.catch {
|
.catch {
|
||||||
onError(it)
|
onError(it)
|
||||||
log.warn(it) { "Failed to delete chapter download for $chapterIds" }
|
log.warn(it) { "Failed to delete chapter download for $chapterIds" }
|
||||||
@@ -63,23 +65,23 @@ class DeleteChapterDownload(
|
|||||||
}
|
}
|
||||||
.collect()
|
.collect()
|
||||||
|
|
||||||
fun asFlow(chapterId: Long) =
|
fun asFlow(chapterId: Long, mangaId: Long?) =
|
||||||
chapterRepository.deleteDownloadedChapter(chapterId)
|
chapterRepository.deleteDownloadedChapter(chapterId)
|
||||||
.onEach { serverListeners.updateChapters(chapterId) }
|
.onEach { serverListeners.updateManga(mangaId ?: -1) }
|
||||||
|
|
||||||
@JvmName("asFlowChapter")
|
@JvmName("asFlowChapter")
|
||||||
fun asFlow(chapter: Chapter) =
|
fun asFlow(chapter: Chapter) =
|
||||||
chapterRepository.deleteDownloadedChapter(chapter.id)
|
chapterRepository.deleteDownloadedChapter(chapter.id)
|
||||||
.onEach { serverListeners.updateChapters(chapter.id) }
|
.onEach { serverListeners.updateManga(chapter.mangaId) }
|
||||||
|
|
||||||
fun asFlow(chapterIds: List<Long>) =
|
fun asFlow(chapterIds: List<Long>, mangaIds: List<Long>?) =
|
||||||
chapterRepository.deleteDownloadedChapters(chapterIds)
|
chapterRepository.deleteDownloadedChapters(chapterIds)
|
||||||
.onEach { serverListeners.updateChapters(chapterIds) }
|
.onEach { serverListeners.updateManga(mangaIds.orEmpty()) }
|
||||||
|
|
||||||
@JvmName("asFlowChapters")
|
@JvmName("asFlowChapters")
|
||||||
fun asFlow(chapter: List<Chapter>) =
|
fun asFlow(chapter: List<Chapter>) =
|
||||||
chapterRepository.deleteDownloadedChapters(chapter.map { it.id })
|
chapterRepository.deleteDownloadedChapters(chapter.map { it.id })
|
||||||
.onEach { serverListeners.updateChapters(chapter.map { it.id }) }
|
.onEach { serverListeners.updateManga(chapter.map { it.mangaId }) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ class GetChapter(
|
|||||||
) {
|
) {
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
onError: suspend (Throwable) -> Unit = {},
|
onError: suspend (Throwable) -> Unit = {},
|
||||||
) = asFlow(chapterId)
|
) = asFlow(chapterId, mangaId)
|
||||||
.take(1)
|
.take(1)
|
||||||
.catch {
|
.catch {
|
||||||
onError(it)
|
onError(it)
|
||||||
@@ -42,16 +43,16 @@ class GetChapter(
|
|||||||
}
|
}
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
|
|
||||||
fun asFlow(chapterId: Long) =
|
fun asFlow(chapterId: Long, mangaId: Long?) =
|
||||||
serverListeners.combineChapters(
|
serverListeners.combineMangaUpdates(
|
||||||
chapterRepository.getChapter(chapterId),
|
chapterRepository.getChapter(chapterId),
|
||||||
chapterIdPredate = { ids -> chapterId in ids },
|
predate = { ids -> mangaId in ids },
|
||||||
)
|
)
|
||||||
|
|
||||||
fun asFlow(chapter: Chapter) =
|
fun asFlow(chapter: Chapter) =
|
||||||
serverListeners.combineChapters(
|
serverListeners.combineMangaUpdates(
|
||||||
chapterRepository.getChapter(chapter.id),
|
chapterRepository.getChapter(chapter.id),
|
||||||
chapterIdPredate = { ids -> chapter.id in ids },
|
predate = { ids -> chapter.mangaId in ids },
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -43,15 +43,15 @@ class GetChapters(
|
|||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
|
|
||||||
fun asFlow(mangaId: Long) =
|
fun asFlow(mangaId: Long) =
|
||||||
serverListeners.combineChapters(
|
serverListeners.combineMangaUpdates(
|
||||||
chapterRepository.getChapters(mangaId),
|
chapterRepository.getChapters(mangaId),
|
||||||
chapterIdPredate = { ids -> false }, // todo
|
predate = { ids -> mangaId in ids },
|
||||||
)
|
)
|
||||||
|
|
||||||
fun asFlow(manga: Manga) =
|
fun asFlow(manga: Manga) =
|
||||||
serverListeners.combineChapters(
|
serverListeners.combineMangaUpdates(
|
||||||
chapterRepository.getChapters(manga.id),
|
chapterRepository.getChapters(manga.id),
|
||||||
chapterIdPredate = { ids -> false }, // todo
|
predate = { ids -> manga.id in ids },
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ class RefreshChapters(
|
|||||||
}
|
}
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
|
|
||||||
fun asFlow(mangaId: Long) =
|
fun asFlow(mangaId: Long, ) =
|
||||||
chapterRepository.fetchChapters(mangaId)
|
chapterRepository.fetchChapters(mangaId)
|
||||||
.onEach { serverListeners.updateChapters(mangaId) }
|
.onEach { serverListeners.updateManga(mangaId) }
|
||||||
|
|
||||||
fun asFlow(manga: Manga) =
|
fun asFlow(manga: Manga) =
|
||||||
chapterRepository.fetchChapters(manga.id)
|
chapterRepository.fetchChapters(manga.id)
|
||||||
.onEach { serverListeners.updateChapters(manga.id) }
|
.onEach { serverListeners.updateManga(manga.id) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -23,11 +23,12 @@ class UpdateChapter(
|
|||||||
) {
|
) {
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
bookmarked: Boolean? = null,
|
bookmarked: Boolean? = null,
|
||||||
read: Boolean? = null,
|
read: Boolean? = null,
|
||||||
lastPageRead: Int? = null,
|
lastPageRead: Int? = null,
|
||||||
onError: suspend (Throwable) -> Unit = {},
|
onError: suspend (Throwable) -> Unit = {},
|
||||||
) = asFlow(chapterId, bookmarked, read, lastPageRead)
|
) = asFlow(chapterId, mangaId, bookmarked, read, lastPageRead)
|
||||||
.catch {
|
.catch {
|
||||||
onError(it)
|
onError(it)
|
||||||
log.warn(it) { "Failed to update chapter bookmark for chapter $chapterId" }
|
log.warn(it) { "Failed to update chapter bookmark for chapter $chapterId" }
|
||||||
@@ -49,11 +50,12 @@ class UpdateChapter(
|
|||||||
|
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
chapterIds: List<Long>,
|
chapterIds: List<Long>,
|
||||||
|
mangaIds: List<Long>?,
|
||||||
bookmarked: Boolean? = null,
|
bookmarked: Boolean? = null,
|
||||||
read: Boolean? = null,
|
read: Boolean? = null,
|
||||||
lastPageRead: Int? = null,
|
lastPageRead: Int? = null,
|
||||||
onError: suspend (Throwable) -> Unit = {},
|
onError: suspend (Throwable) -> Unit = {},
|
||||||
) = asFlow(chapterIds, bookmarked, read, lastPageRead)
|
) = asFlow(chapterIds, mangaIds, bookmarked, read, lastPageRead)
|
||||||
.catch {
|
.catch {
|
||||||
onError(it)
|
onError(it)
|
||||||
log.warn(it) { "Failed to update chapter bookmark for chapters $chapterIds" }
|
log.warn(it) { "Failed to update chapter bookmark for chapters $chapterIds" }
|
||||||
@@ -76,6 +78,7 @@ class UpdateChapter(
|
|||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
bookmarked: Boolean? = null,
|
bookmarked: Boolean? = null,
|
||||||
read: Boolean? = null,
|
read: Boolean? = null,
|
||||||
lastPageRead: Int? = null,
|
lastPageRead: Int? = null,
|
||||||
@@ -84,7 +87,7 @@ class UpdateChapter(
|
|||||||
bookmarked = bookmarked,
|
bookmarked = bookmarked,
|
||||||
read = read,
|
read = read,
|
||||||
lastPageRead = lastPageRead,
|
lastPageRead = lastPageRead,
|
||||||
).onEach { serverListeners.updateChapters(chapterId) }
|
).onEach { serverListeners.updateManga(mangaId ?: -1) }
|
||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapter: Chapter,
|
chapter: Chapter,
|
||||||
@@ -96,10 +99,11 @@ class UpdateChapter(
|
|||||||
bookmarked = bookmarked,
|
bookmarked = bookmarked,
|
||||||
read = read,
|
read = read,
|
||||||
lastPageRead = lastPageRead,
|
lastPageRead = lastPageRead,
|
||||||
).onEach { serverListeners.updateChapters(chapter.id) }
|
).onEach { serverListeners.updateManga(chapter.mangaId) }
|
||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapterIds: List<Long>,
|
chapterIds: List<Long>,
|
||||||
|
mangaIds: List<Long>?,
|
||||||
bookmarked: Boolean? = null,
|
bookmarked: Boolean? = null,
|
||||||
read: Boolean? = null,
|
read: Boolean? = null,
|
||||||
lastPageRead: Int? = null,
|
lastPageRead: Int? = null,
|
||||||
@@ -108,7 +112,7 @@ class UpdateChapter(
|
|||||||
bookmarked = bookmarked,
|
bookmarked = bookmarked,
|
||||||
read = read,
|
read = read,
|
||||||
lastPageRead = lastPageRead,
|
lastPageRead = lastPageRead,
|
||||||
).onEach { serverListeners.updateChapters(chapterIds) }
|
).onEach { serverListeners.updateManga(mangaIds.orEmpty()) }
|
||||||
|
|
||||||
@JvmName("asFlowChapters")
|
@JvmName("asFlowChapters")
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
@@ -121,7 +125,7 @@ class UpdateChapter(
|
|||||||
bookmarked = bookmarked,
|
bookmarked = bookmarked,
|
||||||
read = read,
|
read = read,
|
||||||
lastPageRead = lastPageRead,
|
lastPageRead = lastPageRead,
|
||||||
).onEach { serverListeners.updateChapters(chapters.map { it.id }) }
|
).onEach { serverListeners.updateManga(chapters.map { it.mangaId }.distinct()) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ class UpdateChapterLastPageRead(
|
|||||||
) {
|
) {
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
lastPageRead: Int,
|
lastPageRead: Int,
|
||||||
onError: suspend (Throwable) -> Unit = {},
|
onError: suspend (Throwable) -> Unit = {},
|
||||||
) = asFlow(chapterId, lastPageRead)
|
) = asFlow(chapterId, mangaId, lastPageRead)
|
||||||
.catch {
|
.catch {
|
||||||
onError(it)
|
onError(it)
|
||||||
log.warn(it) { "Failed to update chapter last page read for chapter $chapterId" }
|
log.warn(it) { "Failed to update chapter last page read for chapter $chapterId" }
|
||||||
@@ -44,11 +45,12 @@ class UpdateChapterLastPageRead(
|
|||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
lastPageRead: Int,
|
lastPageRead: Int,
|
||||||
) = chapterRepository.updateChapter(
|
) = chapterRepository.updateChapter(
|
||||||
chapterId = chapterId,
|
chapterId = chapterId,
|
||||||
lastPageRead = lastPageRead,
|
lastPageRead = lastPageRead,
|
||||||
).onEach { serverListeners.updateChapters(chapterId) }
|
).onEach { serverListeners.updateManga(mangaId ?: -1) }
|
||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapter: Chapter,
|
chapter: Chapter,
|
||||||
@@ -56,7 +58,7 @@ class UpdateChapterLastPageRead(
|
|||||||
) = chapterRepository.updateChapter(
|
) = chapterRepository.updateChapter(
|
||||||
chapterId = chapter.id,
|
chapterId = chapter.id,
|
||||||
lastPageRead = lastPageRead,
|
lastPageRead = lastPageRead,
|
||||||
).onEach { serverListeners.updateChapters(chapter.id) }
|
).onEach { serverListeners.updateManga(chapter.mangaId) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class UpdateChapterMeta(
|
|||||||
"juiPageOffset",
|
"juiPageOffset",
|
||||||
pageOffset.toString(),
|
pageOffset.toString(),
|
||||||
).collect()
|
).collect()
|
||||||
serverListeners.updateChapters(chapter.id)
|
serverListeners.updateManga(chapter.mangaId)
|
||||||
}
|
}
|
||||||
emit(Unit)
|
emit(Unit)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ class UpdateChapterRead(
|
|||||||
) {
|
) {
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
read: Boolean,
|
read: Boolean,
|
||||||
onError: suspend (Throwable) -> Unit = {},
|
onError: suspend (Throwable) -> Unit = {},
|
||||||
) = asFlow(chapterId, read)
|
) = asFlow(chapterId, mangaId, read)
|
||||||
.catch {
|
.catch {
|
||||||
onError(it)
|
onError(it)
|
||||||
log.warn(it) { "Failed to update chapter read status for chapter $chapterId" }
|
log.warn(it) { "Failed to update chapter read status for chapter $chapterId" }
|
||||||
@@ -44,19 +45,21 @@ class UpdateChapterRead(
|
|||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapterId: Long,
|
chapterId: Long,
|
||||||
|
mangaId: Long?,
|
||||||
read: Boolean,
|
read: Boolean,
|
||||||
) = chapterRepository.updateChapter(
|
) = chapterRepository.updateChapter(
|
||||||
chapterId = chapterId,
|
chapterId = chapterId,
|
||||||
read = read,
|
read = read,
|
||||||
).onEach { serverListeners.updateChapters(chapterId) }
|
).onEach { serverListeners.updateManga(mangaId ?: -1) }
|
||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapterIds: List<Long>,
|
chapterIds: List<Long>,
|
||||||
|
mangaIds: List<Long>?,
|
||||||
read: Boolean,
|
read: Boolean,
|
||||||
) = chapterRepository.updateChapters(
|
) = chapterRepository.updateChapters(
|
||||||
chapterIds = chapterIds,
|
chapterIds = chapterIds,
|
||||||
read = read,
|
read = read,
|
||||||
).onEach { serverListeners.updateChapters(chapterIds) }
|
).onEach { serverListeners.updateManga(mangaIds.orEmpty()) }
|
||||||
|
|
||||||
fun asFlow(
|
fun asFlow(
|
||||||
chapter: Chapter,
|
chapter: Chapter,
|
||||||
@@ -64,7 +67,7 @@ class UpdateChapterRead(
|
|||||||
) = chapterRepository.updateChapter(
|
) = chapterRepository.updateChapter(
|
||||||
chapterId = chapter.id,
|
chapterId = chapter.id,
|
||||||
read = read,
|
read = read,
|
||||||
).onEach { serverListeners.updateChapters(chapter.id) }
|
).onEach { serverListeners.updateManga(chapter.mangaId) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -23,12 +23,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.coroutines.flow.runningFold
|
import kotlinx.coroutines.flow.runningFold
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
@@ -96,20 +93,20 @@ class UpdatesPager(
|
|||||||
|
|
||||||
private val changedChapters = MutableStateFlow(emptyMap<Long, Chapter>())
|
private val changedChapters = MutableStateFlow(emptyMap<Long, Chapter>())
|
||||||
|
|
||||||
init {
|
// init {
|
||||||
serverListeners.chapterIdsListener
|
// serverListeners.chapterIdsListener
|
||||||
.onEach { updatedChapterIds ->
|
// .onEach { updatedChapterIds ->
|
||||||
val chapters = coroutineScope {
|
// val chapters = coroutineScope {
|
||||||
updatedChapterIds.mapNotNull { id -> chapterIds.value.find { it == id } }.map {
|
// updatedChapterIds.mapNotNull { id -> chapterIds.value.find { it == id } }.map {
|
||||||
async {
|
// async {
|
||||||
getChapter.await(it)
|
// getChapter.await(it)
|
||||||
}
|
// }
|
||||||
}.awaitAll().filterNotNull().associateBy { it.id }
|
// }.awaitAll().filterNotNull().associateBy { it.id }
|
||||||
}
|
// }
|
||||||
changedChapters.update { it + chapters }
|
// changedChapters.update { it + chapters }
|
||||||
}
|
// }
|
||||||
.launchIn(this)
|
// .launchIn(this)
|
||||||
}
|
// }
|
||||||
|
|
||||||
val updates = combine(
|
val updates = combine(
|
||||||
foldedUpdates,
|
foldedUpdates,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import androidx.compose.animation.core.MutableTransitionState
|
|||||||
import androidx.compose.animation.core.TweenSpec
|
import androidx.compose.animation.core.TweenSpec
|
||||||
import androidx.compose.animation.core.animateDp
|
import androidx.compose.animation.core.animateDp
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.rememberTransition
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.core.updateTransition
|
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
@@ -408,7 +408,7 @@ fun ExpandablePreference(
|
|||||||
targetState = !expanded
|
targetState = !expanded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val transition = updateTransition(transitionState)
|
val transition = rememberTransition(transitionState)
|
||||||
val elevation by transition.animateDp({
|
val elevation by transition.animateDp({
|
||||||
tween(durationMillis = EXPAND_ANIMATION_DURATION)
|
tween(durationMillis = EXPAND_ANIMATION_DURATION)
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
package ca.gosyer.jui.ui.base.state
|
package ca.gosyer.jui.ui.base.state
|
||||||
|
|
||||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||||
|
import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi
|
||||||
import kotlinx.coroutines.InternalCoroutinesApi
|
import kotlinx.coroutines.InternalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.internal.SynchronizedObject
|
import kotlinx.coroutines.internal.SynchronizedObject
|
||||||
@@ -39,6 +40,7 @@ class SavedStateHandleDelegate<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
|
||||||
class SavedStateHandleStateFlow<T>(
|
class SavedStateHandleStateFlow<T>(
|
||||||
private val key: String,
|
private val key: String,
|
||||||
private val savedStateHandle: SavedStateHandle,
|
private val savedStateHandle: SavedStateHandle,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.ui.library.components
|
package ca.gosyer.jui.ui.library.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.PagerState
|
import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -119,7 +118,5 @@ private fun LibraryLoadedPage(
|
|||||||
showLanguage = showLanguage,
|
showLanguage = showLanguage,
|
||||||
showLocal = showLocal,
|
showLocal = showLocal,
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> Box {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ class MangaScreenViewModel(
|
|||||||
) {
|
) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
manga.value?.let {
|
manga.value?.let {
|
||||||
updateChapter.await(chapterIds, read = read, onError = { toast(it.message.orEmpty()) })
|
updateChapter.await(chapterIds, listOf(params.mangaId), read = read, onError = { toast(it.message.orEmpty()) })
|
||||||
selectedIds.value = persistentListOf()
|
selectedIds.value = persistentListOf()
|
||||||
loadChapters()
|
loadChapters()
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,7 @@ class MangaScreenViewModel(
|
|||||||
) {
|
) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
manga.value?.let {
|
manga.value?.let {
|
||||||
updateChapter.await(chapterIds, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
updateChapter.await(chapterIds, listOf(params.mangaId), bookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||||
selectedIds.value = persistentListOf()
|
selectedIds.value = persistentListOf()
|
||||||
loadChapters()
|
loadChapters()
|
||||||
}
|
}
|
||||||
@@ -325,7 +325,7 @@ class MangaScreenViewModel(
|
|||||||
val chapters = chapters.value
|
val chapters = chapters.value
|
||||||
.sortedBy { it.chapter.index }
|
.sortedBy { it.chapter.index }
|
||||||
.subList(0, index).map { it.chapter.id } // todo test
|
.subList(0, index).map { it.chapter.id } // todo test
|
||||||
updateChapter.await(chapters, read = true, onError = { toast(it.message.orEmpty()) })
|
updateChapter.await(chapters, listOf(params.mangaId), read = true, onError = { toast(it.message.orEmpty()) })
|
||||||
selectedIds.value = persistentListOf()
|
selectedIds.value = persistentListOf()
|
||||||
loadChapters()
|
loadChapters()
|
||||||
}
|
}
|
||||||
@@ -340,7 +340,7 @@ class MangaScreenViewModel(
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
val chapterIds = selectedIds.value
|
val chapterIds = selectedIds.value
|
||||||
deleteChapterDownload.await(chapterIds, onError = { toast(it.message.orEmpty()) })
|
deleteChapterDownload.await(chapterIds, listOf(params.mangaId), onError = { toast(it.message.orEmpty()) })
|
||||||
selectedItems.value.forEach {
|
selectedItems.value.forEach {
|
||||||
it.setNotDownloaded()
|
it.setNotDownloaded()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ import kotlinx.collections.immutable.ImmutableMap
|
|||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import kotlinx.collections.immutable.toImmutableMap
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@@ -137,6 +138,7 @@ expect class SettingsServerHostViewModel : ViewModel
|
|||||||
@Composable
|
@Composable
|
||||||
expect fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit
|
expect fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
|
||||||
private class ServerSettingMutableStateFlow<T>(
|
private class ServerSettingMutableStateFlow<T>(
|
||||||
parent: StateFlow<Settings>,
|
parent: StateFlow<Settings>,
|
||||||
getSetting: (Settings) -> T,
|
getSetting: (Settings) -> T,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
|
||||||
|
@Suppress("DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_WARNING")
|
||||||
sealed class SourceFiltersView<T : SourceFilter, R : Any?> {
|
sealed class SourceFiltersView<T : SourceFilter, R : Any?> {
|
||||||
abstract val index: Int
|
abstract val index: Int
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
|
||||||
|
@Suppress("DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_WARNING")
|
||||||
sealed class SourceSettingsView<T : SourcePreference, R : Any?> {
|
sealed class SourceSettingsView<T : SourcePreference, R : Any?> {
|
||||||
abstract val index: Int
|
abstract val index: Int
|
||||||
abstract val title: String?
|
abstract val title: String?
|
||||||
|
|||||||
@@ -118,7 +118,9 @@ class UpdatesScreenViewModel(
|
|||||||
read: Boolean,
|
read: Boolean,
|
||||||
) {
|
) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
updateChapter.await(chapterIds, read = read, onError = { toast(it.message.orEmpty()) })
|
val mangaIds = updates.value.filterIsInstance<UpdatesUI.Item>().filter { it.chapterDownloadItem.chapter.id in chapterIds }
|
||||||
|
.mapNotNull { it.chapterDownloadItem.manga?.id }
|
||||||
|
updateChapter.await(chapterIds, mangaIds, read = read, onError = { toast(it.message.orEmpty()) })
|
||||||
selectedIds.value = persistentListOf()
|
selectedIds.value = persistentListOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +134,9 @@ class UpdatesScreenViewModel(
|
|||||||
bookmark: Boolean,
|
bookmark: Boolean,
|
||||||
) {
|
) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
updateChapter.await(chapterIds, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
val mangaIds = updates.value.filterIsInstance<UpdatesUI.Item>().filter { it.chapterDownloadItem.chapter.id in chapterIds }
|
||||||
|
.mapNotNull { it.chapterDownloadItem.manga?.id }
|
||||||
|
updateChapter.await(chapterIds, mangaIds, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||||
selectedIds.value = persistentListOf()
|
selectedIds.value = persistentListOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,7 +161,9 @@ class UpdatesScreenViewModel(
|
|||||||
scope.launchDefault {
|
scope.launchDefault {
|
||||||
if (chapter == null) {
|
if (chapter == null) {
|
||||||
val selectedIds = selectedIds.value
|
val selectedIds = selectedIds.value
|
||||||
deleteChapterDownload.await(selectedIds, onError = { toast(it.message.orEmpty()) })
|
val mangaIds = updates.value.filterIsInstance<UpdatesUI.Item>().filter { it.chapterDownloadItem.chapter.id in selectedIds }
|
||||||
|
.mapNotNull { it.chapterDownloadItem.manga?.id }
|
||||||
|
deleteChapterDownload.await(selectedIds, mangaIds, onError = { toast(it.message.orEmpty()) })
|
||||||
selectedItems.value.forEach {
|
selectedItems.value.forEach {
|
||||||
it.setNotDownloaded()
|
it.setNotDownloaded()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,10 +108,6 @@ fun ImageLoaderImage(
|
|||||||
progress.value = 1.0F
|
progress.value = 1.0F
|
||||||
ImageLoaderImageState.Success
|
ImageLoaderImageState.Success
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
|
||||||
ImageLoaderImageState.Loading
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Crossfade(state, animationSpec = animationSpec, modifier = modifier) {
|
Crossfade(state, animationSpec = animationSpec, modifier = modifier) {
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ package ca.gosyer.jui.uicore.prefs
|
|||||||
|
|
||||||
import ca.gosyer.jui.core.prefs.Preference
|
import ca.gosyer.jui.core.prefs.Preference
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
|
||||||
class PreferenceMutableStateFlow<T>(
|
class PreferenceMutableStateFlow<T>(
|
||||||
private val preference: Preference<T>,
|
private val preference: Preference<T>,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import androidx.compose.runtime.Stable
|
|||||||
import ca.gosyer.jui.core.lang.launchDefault
|
import ca.gosyer.jui.core.lang.launchDefault
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import dev.icerock.moko.resources.format
|
import dev.icerock.moko.resources.format
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
@@ -26,6 +27,7 @@ actual class ContextWrapper {
|
|||||||
vararg args: Any,
|
vararg args: Any,
|
||||||
): String = stringResource.format(*args).localized()
|
): String = stringResource.format(*args).localized()
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
actual fun toast(
|
actual fun toast(
|
||||||
string: String,
|
string: String,
|
||||||
length: Length,
|
length: Length,
|
||||||
|
|||||||
Reference in New Issue
Block a user