Support batch chapter update api

This commit is contained in:
Syer10
2022-11-15 14:27:46 -05:00
parent 49bee53a67
commit fab3907d08
14 changed files with 473 additions and 65 deletions

View File

@@ -0,0 +1,250 @@
/*
* 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.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.ChapterRepository
import ca.gosyer.jui.domain.manga.model.Manga
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName
class BatchUpdateChapter @Inject constructor(private val chapterRepository: ChapterRepository) {
@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) {
chapterRepository.batchUpdateChapter(
mangaId,
MangaChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete
)
)
)
} else {
chapterRepository.batchUpdateChapter(
ChapterBatchEditInput(
chapterIds = chapterIds,
change = ChapterChange(
isRead = isRead,
isBookmarked = isBookmarked,
lastPageRead = lastPageRead,
delete = delete
)
)
)
}
companion object {
private val log = logging()
}
}

View File

@@ -0,0 +1,15 @@
/*
* 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.model
import kotlinx.serialization.Serializable
@Serializable
data class ChapterBatchEditInput(
val chapterIds: List<Long>? = null,
val change: ChapterChange?
)

View File

@@ -0,0 +1,17 @@
/*
* 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.model
import kotlinx.serialization.Serializable
@Serializable
data class ChapterChange(
val isRead: Boolean? = null,
val isBookmarked: Boolean? = null,
val lastPageRead: Int? = null,
val delete: Boolean? = null
)

View File

@@ -0,0 +1,16 @@
/*
* 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.model
import kotlinx.serialization.Serializable
@Serializable
data class MangaChapterBatchEditInput(
val chapterIds: List<Long>? = null,
val chapterIndexes: List<Int>? = null,
val change: ChapterChange?
)

View File

@@ -7,11 +7,16 @@
package ca.gosyer.jui.domain.chapter.service
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.model.ChapterBatchEditInput
import ca.gosyer.jui.domain.chapter.model.MangaChapterBatchEditInput
import de.jensklingenberg.ktorfit.http.Body
import de.jensklingenberg.ktorfit.http.DELETE
import de.jensklingenberg.ktorfit.http.Field
import de.jensklingenberg.ktorfit.http.FormUrlEncoded
import de.jensklingenberg.ktorfit.http.GET
import de.jensklingenberg.ktorfit.http.Headers
import de.jensklingenberg.ktorfit.http.PATCH
import de.jensklingenberg.ktorfit.http.POST
import de.jensklingenberg.ktorfit.http.Path
import de.jensklingenberg.ktorfit.http.Query
import de.jensklingenberg.ktorfit.http.ReqBuilder
@@ -44,6 +49,19 @@ interface ChapterRepository {
@Field("markPrevRead") markPreviousRead: Boolean? = null
): Flow<HttpResponse>
@POST("api/v1/manga/{mangaId}/chapter/batch")
@Headers("Content-Type: application/json")
fun batchUpdateChapter(
@Path("mangaId") mangaId: Long,
@Body input: MangaChapterBatchEditInput
): Flow<HttpResponse>
@POST("api/v1/chapter/batch")
@Headers("Content-Type: application/json")
fun batchUpdateChapter(
@Body input: ChapterBatchEditInput
): Flow<HttpResponse>
@GET("api/v1/manga/{mangaId}/chapter/{chapterIndex}/page/{pageNum}")
fun getPage(
@Path("mangaId") mangaId: Long,

View File

@@ -0,0 +1,39 @@
/*
* 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.manga.interactor
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.manga.service.MangaRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetMangaFull @Inject constructor(private val mangaRepository: MangaRepository) {
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to get full manga $mangaId" }
}
.singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to get full manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = mangaRepository.getMangaFull(mangaId)
fun asFlow(manga: Manga) = mangaRepository.getMangaFull(manga.id)
companion object {
private val log = logging()
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.manga.interactor
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.manga.service.MangaRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class RefreshMangaFull @Inject constructor(private val mangaRepository: MangaRepository) {
suspend fun await(mangaId: Long, onError: suspend (Throwable) -> Unit = {}) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to refresh full manga $mangaId" }
}
.singleOrNull()
suspend fun await(manga: Manga, onError: suspend (Throwable) -> Unit = {}) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to refresh full manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = mangaRepository.getMangaFull(mangaId, true)
fun asFlow(manga: Manga) = mangaRepository.getMangaFull(manga.id, true)
companion object {
private val log = logging()
}
}

View File

@@ -8,6 +8,7 @@ package ca.gosyer.jui.domain.manga.model
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.source.model.Source
import ca.gosyer.jui.i18n.MR
import dev.icerock.moko.resources.StringResource
@@ -33,14 +34,15 @@ data class Manga(
val freshData: Boolean,
val meta: MangaMeta,
val realUrl: String?,
val lastFetchedAt: Long? = 0, // todo remove default
val chaptersLastFetchedAt: Long? = 0, // todo remove default
val lastFetchedAt: Long?,
val chaptersLastFetchedAt: Long?,
val inLibraryAt: Long,
val unreadCount: Int?,
val downloadCount: Int?,
val chapterCount: Int? = null, // todo remove default
val age: Long? = null, // todo remove default
val chaptersAge: Long? = null // todo remove default
val chapterCount: Int?,
var lastChapterRead: Chapter?,
val age: Long?,
val chaptersAge: Long?
)
@Serializable

View File

@@ -26,6 +26,12 @@ interface MangaRepository {
@Query("onlineFetch") refresh: Boolean = false
): Flow<Manga>
@GET("api/v1/manga/{mangaId}/full")
fun getMangaFull(
@Path("mangaId") mangaId: Long,
@Query("onlineFetch") refresh: Boolean = false
): Flow<Manga>
@GET("api/v1/manga/{mangaId}/thumbnail")
fun getMangaThumbnail(
@Path("mangaId") mangaId: Long,