Chapters graphql, reader is broken

This commit is contained in:
Syer10
2024-09-02 18:08:57 -04:00
parent 856aa34aad
commit 478c58a9ac
37 changed files with 791 additions and 892 deletions

View File

@@ -15,7 +15,6 @@ 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
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.onStart
@@ -35,12 +34,7 @@ class ServerListeners
)
val mangaListener = _mangaListener.asSharedFlow()
private val _chapterIndexesListener = MutableSharedFlow<Pair<Long, List<Int>?>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIndexesListener = _chapterIndexesListener.asSharedFlow()
private val _chapterIdsListener = MutableSharedFlow<Pair<Long?, List<Long>>>(
private val _chapterIdsListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIdsListener = _chapterIdsListener.asSharedFlow()
@@ -91,58 +85,32 @@ class ServerListeners
fun <T> combineChapters(
flow: Flow<T>,
indexPredate: (suspend (Long, List<Int>?) -> Boolean)? = null,
idPredate: (suspend (Long?, List<Long>) -> Boolean)? = null,
idPredate: (suspend (List<Long>) -> Boolean)? = null,
): Flow<T> {
val indexListener = if (indexPredate != null) {
_chapterIndexesListener.filter { indexPredate(it.first, it.second) }.startWith(Unit)
} else {
_chapterIndexesListener.startWith(Unit)
}
val idsListener = if (idPredate != null) {
_chapterIdsListener.filter { idPredate(it.first, it.second) }.startWith(Unit)
_chapterIdsListener.filter { idPredate(it) }.startWith(Unit)
} else {
_chapterIdsListener.startWith(Unit)
}
return combine(indexListener, idsListener) { _, _ -> }
return idsListener
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
}
fun updateChapters(
mangaId: Long,
chapterIndexes: List<Int>,
) {
scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.ifEmpty { null })
}
}
fun updateChapters(
mangaId: Long,
vararg chapterIndexes: Int,
) {
scope.launch {
_chapterIndexesListener.emit(mangaId to chapterIndexes.toList().ifEmpty { null })
}
}
fun updateChapters(
mangaId: Long?,
chapterIds: List<Long>,
) {
scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds)
_chapterIdsListener.emit(chapterIds)
}
}
fun updateChapters(
mangaId: Long?,
vararg chapterIds: Long,
) {
scope.launch {
_chapterIdsListener.emit(mangaId to chapterIds.toList())
_chapterIdsListener.emit(chapterIds.toList())
}
}

View File

@@ -1,256 +0,0 @@
/*
* 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.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.model.ChapterBatchEditInput
import ca.gosyer.jui.domain.chapter.model.ChapterChange
import ca.gosyer.jui.domain.chapter.model.MangaChapterBatchEditInput
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName
class BatchUpdateChapter
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val serverListeners: ServerListeners,
) {
@JvmName("awaitChapters")
suspend fun await(
mangaId: Long,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of $mangaId" }
}
.collect()
suspend fun await(
mangaId: Long,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of $mangaId" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
manga: Manga,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of ${manga.title}(${manga.id})" }
}
.collect()
suspend fun await(
manga: Manga,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters of ${manga.title}(${manga.id})" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update multiple chapters" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds, isRead, isBookmarked, lastPageRead, delete)
.catch {
onError(it)
log.warn(it) { "Failed to update update multiple chapters" }
}
.collect()
@JvmName("asFlowChapters")
fun asFlow(
mangaId: Long,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = mangaId,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
mangaId: Long,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = mangaId,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
@JvmName("asFlowChapters")
fun asFlow(
manga: Manga,
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = manga.id,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
manga: Manga,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = manga.id,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
@JvmName("asFlowChapters")
fun asFlow(
chapters: List<Chapter>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = null,
chapterIds = chapters.map { it.id },
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
fun asFlow(
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = getFlow(
mangaId = null,
chapterIds = chapterIds,
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
)
private fun getFlow(
mangaId: Long?,
chapterIds: List<Long>,
isRead: Boolean? = null,
isBookmarked: Boolean? = null,
lastPageRead: Int? = null,
delete: Boolean? = null,
) = if (mangaId != null) {
chapterRepositoryOld.batchUpdateChapter(
mangaId,
MangaChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
),
),
)
} else {
chapterRepositoryOld.batchUpdateChapter(
ChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete,
),
),
)
}.onEach {
serverListeners.updateChapters(mangaId, chapterIds)
}
companion object {
private val log = logging()
}
}

View File

@@ -8,42 +8,31 @@ package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName
class DeleteChapterDownload
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
index: Int,
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for $index of $mangaId" }
}
.collect()
suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for $index of ${manga.title}(${manga.id})" }
log.warn(it) { "Failed to delete chapter download for $chapterId" }
}
.collect()
@JvmName("awaitChapter")
suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
@@ -54,21 +43,46 @@ class DeleteChapterDownload
}
.collect()
fun asFlow(
mangaId: Long,
index: Int,
) = chapterRepositoryOld.deleteChapterDownload(mangaId, index)
.onEach { serverListeners.updateChapters(mangaId, index) }
suspend fun await(
chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for $chapterIds" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for ${chapters.joinToString { it.id.toString() }}" }
}
.collect()
fun asFlow(
manga: Manga,
index: Int,
) = chapterRepositoryOld.deleteChapterDownload(manga.id, index)
.onEach { serverListeners.updateChapters(manga.id, index) }
chapterId: Long,
) = chapterRepository.deleteDownloadedChapter(chapterId)
.onEach { serverListeners.updateChapters(chapterId) }
@JvmName("asFlowChapter")
fun asFlow(chapter: Chapter) =
chapterRepositoryOld.deleteChapterDownload(chapter.mangaId, chapter.index)
.onEach { serverListeners.updateChapters(chapter.mangaId, chapter.index) }
chapterRepository.deleteDownloadedChapter(chapter.id)
.onEach { serverListeners.updateChapters(chapter.id) }
fun asFlow(
chapterIds: List<Long>,
) = chapterRepository.deleteDownloadedChapters(chapterIds)
.onEach { serverListeners.updateChapters(chapterIds) }
@JvmName("asFlowChapters")
fun asFlow(chapter: List<Chapter>) =
chapterRepository.deleteDownloadedChapters(chapter.map { it.id })
.onEach { serverListeners.updateChapters(chapter.map { it.id }) }
companion object {
private val log = logging()

View File

@@ -8,8 +8,7 @@ package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
import kotlinx.coroutines.flow.take
@@ -19,30 +18,17 @@ import org.lighthousegames.logging.logging
class GetChapter
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
index: Int,
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
) = asFlow(chapterId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $index for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $index for ${manga.title}(${manga.id})" }
log.warn(it) { "Failed to get chapter $chapterId" }
}
.singleOrNull()
@@ -58,34 +44,16 @@ class GetChapter
.singleOrNull()
fun asFlow(
mangaId: Long,
index: Int,
chapterId: Long,
) = serverListeners.combineChapters(
chapterRepositoryOld.getChapter(mangaId, index),
indexPredate = { id, chapterIndexes ->
id == mangaId && (chapterIndexes == null || index in chapterIndexes)
},
idPredate = { id, _ -> id == mangaId },
)
fun asFlow(
manga: Manga,
index: Int,
) = serverListeners.combineChapters(
chapterRepositoryOld.getChapter(manga.id, index),
indexPredate = { id, chapterIndexes ->
id == manga.id && (chapterIndexes == null || index in chapterIndexes)
},
idPredate = { id, _ -> id == manga.id },
chapterRepository.getChapter(chapterId),
idPredate = { ids -> chapterId in ids },
)
fun asFlow(chapter: Chapter) =
serverListeners.combineChapters(
chapterRepositoryOld.getChapter(chapter.mangaId, chapter.index),
indexPredate = { id, chapterIndexes ->
id == chapter.mangaId && (chapterIndexes == null || chapter.index in chapterIndexes)
},
idPredate = { id, _ -> id == chapter.mangaId },
chapterRepository.getChapter(chapter.id),
idPredate = { ids -> chapter.id in ids },
)
companion object {

View File

@@ -1,84 +0,0 @@
/*
* 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.chapter.interactor
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import io.ktor.client.request.HttpRequestBuilder
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapterPage
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
) {
suspend fun await(
mangaId: Long,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter $index for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter $index for ${manga.title}(${manga.id})" }
}
.singleOrNull()
suspend fun await(
chapter: Chapter,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, pageNum, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $pageNum for chapter ${chapter.index} for ${chapter.mangaId}" }
}
.singleOrNull()
fun asFlow(
mangaId: Long,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepositoryOld.getPage(mangaId, index, pageNum, block)
fun asFlow(
manga: Manga,
index: Int,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepositoryOld.getPage(manga.id, index, pageNum, block)
fun asFlow(
chapter: Chapter,
pageNum: Int,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepositoryOld.getPage(chapter.mangaId, chapter.index, pageNum, block)
companion object {
private val log = logging()
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.chapter.interactor
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import io.ktor.client.request.HttpRequestBuilder
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapterPages
@Inject
constructor(
private val chapterRepository: ChapterRepository,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to get pages for $chapterId" }
}
.singleOrNull()
suspend fun await(
url: String,
onError: suspend (Throwable) -> Unit = {},
block: HttpRequestBuilder.() -> Unit,
) = asFlow(url, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $url" }
}
.singleOrNull()
fun asFlow(
chapterId: Long,
) = chapterRepository.getPages(chapterId)
fun asFlow(
url: String,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(url, block)
companion object {
private val log = logging()
}
}

View File

@@ -7,7 +7,7 @@
package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import ca.gosyer.jui.domain.manga.model.Manga
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
@@ -18,7 +18,7 @@ import org.lighthousegames.logging.logging
class GetChapters
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
@@ -45,16 +45,14 @@ class GetChapters
fun asFlow(mangaId: Long) =
serverListeners.combineChapters(
chapterRepositoryOld.getChapters(mangaId),
indexPredate = { id, _ -> id == mangaId },
idPredate = { id, _ -> id == mangaId },
chapterRepository.getChapters(mangaId),
idPredate = { ids -> false }, // todo
)
fun asFlow(manga: Manga) =
serverListeners.combineChapters(
chapterRepositoryOld.getChapters(manga.id),
indexPredate = { id, _ -> id == manga.id },
idPredate = { id, _ -> id == manga.id },
chapterRepository.getChapters(manga.id),
idPredate = { ids -> false }, // todo
)
companion object {

View File

@@ -7,7 +7,7 @@
package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import ca.gosyer.jui.domain.manga.model.Manga
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.onEach
@@ -18,7 +18,7 @@ import org.lighthousegames.logging.logging
class RefreshChapters
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
@@ -42,11 +42,11 @@ class RefreshChapters
.singleOrNull()
fun asFlow(mangaId: Long) =
chapterRepositoryOld.getChapters(mangaId, true)
chapterRepository.fetchChapters(mangaId)
.onEach { serverListeners.updateChapters(mangaId) }
fun asFlow(manga: Manga) =
chapterRepositoryOld.getChapters(manga.id, true)
chapterRepository.fetchChapters(manga.id)
.onEach { serverListeners.updateChapters(manga.id) }
companion object {

View File

@@ -0,0 +1,130 @@
/*
* 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.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName
class UpdateChapter
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter $chapterId" }
}
.collect()
suspend fun await(
chapter: Chapter,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapters $chapterIds" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapters ${chapters.joinToString { it.id.toString() }}" }
}
.collect()
fun asFlow(
chapterId: Long,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapter(
chapterId = chapterId,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapter: Chapter,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null
) = chapterRepository.updateChapter(
chapterId = chapter.id,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapter.id) }
fun asFlow(
chapterIds: List<Long>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapters(
chapterIds = chapterIds,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterIds) }
@JvmName("asFlowChapters")
fun asFlow(
chapters: List<Chapter>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null
) = chapterRepository.updateChapters(
chapterIds = chapters.map { it.id },
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapters.map { it.id }) }
companion object {
private val log = logging()
}
}

View File

@@ -1,92 +0,0 @@
/*
* 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.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterBookmarked
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
index: Int,
bookmarked: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index, bookmarked)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter $index of $mangaId" }
}
.collect()
suspend fun await(
manga: Manga,
index: Int,
bookmarked: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index, bookmarked)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter $index of ${manga.title}(${manga.id})" }
}
.collect()
suspend fun await(
chapter: Chapter,
bookmarked: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, bookmarked)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
fun asFlow(
mangaId: Long,
index: Int,
bookmarked: Boolean,
) = chapterRepositoryOld.updateChapter(
mangaId = mangaId,
chapterIndex = index,
bookmarked = bookmarked,
).onEach { serverListeners.updateChapters(mangaId, index) }
fun asFlow(
manga: Manga,
index: Int,
bookmarked: Boolean,
) = chapterRepositoryOld.updateChapter(
mangaId = manga.id,
chapterIndex = index,
bookmarked = bookmarked,
).onEach { serverListeners.updateChapters(manga.id, index) }
fun asFlow(
chapter: Chapter,
bookmarked: Boolean,
) = chapterRepositoryOld.updateChapter(
mangaId = chapter.mangaId,
chapterIndex = chapter.index,
bookmarked = bookmarked,
).onEach { serverListeners.updateChapters(chapter.mangaId, chapter.index) }
companion object {
private val log = logging()
}
}

View File

@@ -8,8 +8,7 @@ package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
@@ -19,30 +18,17 @@ import org.lighthousegames.logging.logging
class UpdateChapterLastPageRead
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
index: Int,
chapterId: Long,
lastPageRead: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index, lastPageRead)
) = asFlow(chapterId, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter last page read for chapter $index of $mangaId" }
}
.collect()
suspend fun await(
manga: Manga,
index: Int,
lastPageRead: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter last page read for chapter $index of ${manga.title}(${manga.id})" }
log.warn(it) { "Failed to update chapter last page read for chapter $chapterId" }
}
.collect()
@@ -58,33 +44,20 @@ class UpdateChapterLastPageRead
.collect()
fun asFlow(
mangaId: Long,
index: Int,
chapterId: Long,
lastPageRead: Int,
) = chapterRepositoryOld.updateChapter(
mangaId = mangaId,
chapterIndex = index,
) = chapterRepository.updateChapter(
chapterId = chapterId,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(mangaId, index) }
fun asFlow(
manga: Manga,
index: Int,
lastPageRead: Int,
) = chapterRepositoryOld.updateChapter(
mangaId = manga.id,
chapterIndex = index,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(manga.id, index) }
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapter: Chapter,
lastPageRead: Int,
) = chapterRepositoryOld.updateChapter(
mangaId = chapter.mangaId,
chapterIndex = chapter.index,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapter.mangaId, chapter.index) }
).onEach { serverListeners.updateChapters(chapter.id) }
companion object {
private val log = logging()

View File

@@ -1,85 +0,0 @@
/*
* 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.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterMarkPreviousRead
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter $index of $mangaId" }
}
.collect()
suspend fun await(
manga: Manga,
index: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter $index of ${manga.title}(${manga.id})" }
}
.collect()
suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
fun asFlow(
mangaId: Long,
index: Int,
) = chapterRepositoryOld.updateChapter(
mangaId = mangaId,
chapterIndex = index,
markPreviousRead = true,
).onEach { serverListeners.updateChapters(mangaId, index) }
fun asFlow(
manga: Manga,
index: Int,
) = chapterRepositoryOld.updateChapter(
mangaId = manga.id,
chapterIndex = index,
markPreviousRead = true,
).onEach { serverListeners.updateChapters(manga.id, index) }
fun asFlow(chapter: Chapter) =
chapterRepositoryOld.updateChapter(
mangaId = chapter.mangaId,
chapterIndex = chapter.index,
markPreviousRead = true,
).onEach { serverListeners.updateChapters(chapter.mangaId, chapter.index) }
companion object {
private val log = logging()
}
}

View File

@@ -8,7 +8,7 @@ package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
@@ -18,7 +18,7 @@ import org.lighthousegames.logging.logging
class UpdateChapterMeta
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
@@ -37,13 +37,12 @@ class UpdateChapterMeta
pageOffset: Int = chapter.meta.juiPageOffset,
) = flow {
if (pageOffset != chapter.meta.juiPageOffset) {
chapterRepositoryOld.updateChapterMeta(
chapter.mangaId,
chapter.index,
chapterRepository.updateChapterMeta(
chapter.id,
"juiPageOffset",
pageOffset.toString(),
).collect()
serverListeners.updateChapters(chapter.mangaId, chapter.index)
serverListeners.updateChapters(chapter.id)
}
emit(Unit)
}

View File

@@ -8,8 +8,7 @@ package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.ServerListeners
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepositoryOld
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
@@ -19,30 +18,17 @@ import org.lighthousegames.logging.logging
class UpdateChapterRead
@Inject
constructor(
private val chapterRepositoryOld: ChapterRepositoryOld,
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
index: Int,
chapterId: Long,
read: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, index, read)
) = asFlow(chapterId, read)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter $index of $mangaId" }
}
.collect()
suspend fun await(
manga: Manga,
index: Int,
read: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, index, read)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter $index of ${manga.title}(${manga.id})" }
log.warn(it) { "Failed to update chapter read status for chapter $chapterId" }
}
.collect()
@@ -58,33 +44,28 @@ class UpdateChapterRead
.collect()
fun asFlow(
mangaId: Long,
index: Int,
chapterId: Long,
read: Boolean,
) = chapterRepositoryOld.updateChapter(
mangaId = mangaId,
chapterIndex = index,
) = chapterRepository.updateChapter(
chapterId = chapterId,
read = read,
).onEach { serverListeners.updateChapters(mangaId, index) }
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
manga: Manga,
index: Int,
chapterIds: List<Long>,
read: Boolean,
) = chapterRepositoryOld.updateChapter(
mangaId = manga.id,
chapterIndex = index,
) = chapterRepository.updateChapters(
chapterIds = chapterIds,
read = read,
).onEach { serverListeners.updateChapters(manga.id, index) }
).onEach { serverListeners.updateChapters(chapterIds) }
fun asFlow(
chapter: Chapter,
read: Boolean,
) = chapterRepositoryOld.updateChapter(
mangaId = chapter.mangaId,
chapterIndex = chapter.index,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
read = read,
).onEach { serverListeners.updateChapters(chapter.mangaId, chapter.index) }
).onEach { serverListeners.updateChapters(chapter.id) }
companion object {
private val log = logging()

View File

@@ -25,7 +25,6 @@ data class Chapter(
val index: Int,
val fetchedAt: Long,
val realUrl: String?,
val chapterCount: Int?,
val pageCount: Int?,
val lastReadAt: Int?,
val downloaded: Boolean,

View File

@@ -0,0 +1,56 @@
package ca.gosyer.jui.domain.chapter.service
import ca.gosyer.jui.domain.chapter.model.Chapter
import io.ktor.client.request.HttpRequestBuilder
import kotlinx.coroutines.flow.Flow
interface ChapterRepository {
fun getChapter(
chapterId: Long,
): Flow<Chapter>
fun getChapters(
mangaId: Long,
): Flow<List<Chapter>>
fun updateChapter(
chapterId: Long,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
): Flow<Unit>
fun updateChapters(
chapterIds: List<Long>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
): Flow<Unit>
fun deleteDownloadedChapter(
chapterId: Long,
): Flow<Unit>
fun deleteDownloadedChapters(
chapterIds: List<Long>,
): Flow<Unit>
fun updateChapterMeta(
chapterId: Long,
key: String,
value: String,
): Flow<Unit>
fun fetchChapters(
mangaId: Long,
): Flow<List<Chapter>>
fun getPages(
chapterId: Long,
): Flow<List<String>>
fun getPage(
url: String,
block: HttpRequestBuilder.() -> Unit,
): Flow<ByteArray>
}

View File

@@ -81,7 +81,7 @@ class UpdatesPager
updates.filterIsInstance<Updates.Update>().map { it.manga.id }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val chapterIds = foldedUpdates.map { updates ->
updates.filterIsInstance<Updates.Update>().map { Triple(it.manga.id, it.chapter.index, it.chapter.id) }
updates.filterIsInstance<Updates.Update>().map { it.chapter.id }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val changedManga = serverListeners.mangaListener.runningFold(emptyMap<Long, Manga>()) { manga, updatedMangaIds ->
@@ -97,36 +97,12 @@ class UpdatesPager
private val changedChapters = MutableStateFlow(emptyMap<Long, Chapter>())
init {
serverListeners.chapterIndexesListener
.onEach { (mangaId, chapterIndexes) ->
if (chapterIndexes == null) {
val chapters = coroutineScope {
foldedUpdates.value.filterIsInstance<Updates.Update>().filter { it.manga.id == mangaId }.map {
async {
getChapter.await(it.manga.id, it.chapter.index)
}
}.awaitAll().filterNotNull().associateBy { it.id }
}
changedChapters.update { it + chapters }
} else {
val chapters = coroutineScope {
chapterIndexes.mapNotNull { index -> chapterIds.value.find { it.first == mangaId && it.second == index } }
.map {
async {
getChapter.await(it.first, it.second)
}
}.awaitAll().filterNotNull().associateBy { it.id }
}
changedChapters.update { it + chapters }
}
}
.launchIn(this)
serverListeners.chapterIdsListener
.onEach { (_, updatedChapterIds) ->
.onEach { updatedChapterIds ->
val chapters = coroutineScope {
updatedChapterIds.mapNotNull { id -> chapterIds.value.find { it.third == id } }.map {
updatedChapterIds.mapNotNull { id -> chapterIds.value.find { it == id } }.map {
async {
getChapter.await(it.first, it.second)
getChapter.await(it)
}
}.awaitAll().filterNotNull().associateBy { it.id }
}