mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2026-01-15 16:22:32 +01:00
Support batch chapter update api
This commit is contained in:
@@ -6,9 +6,9 @@ object Config {
|
||||
// Tachidesk-Server version
|
||||
const val tachideskVersion = "v0.6.5"
|
||||
// Match this to the Tachidesk-Server commit count
|
||||
const val serverCode = 1148
|
||||
const val serverCode = 1156
|
||||
const val preview = true
|
||||
const val previewCommit = "2195c3df765c3e1e435595d9edbec8ad3590bf46"
|
||||
const val previewCommit = "67e09e2e1d452e041c46a334f1b473f38c5fc25b"
|
||||
|
||||
val desktopJvmTarget = JavaVersion.VERSION_17
|
||||
val androidJvmTarget = JavaVersion.VERSION_11
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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?
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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?
|
||||
)
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -87,6 +87,10 @@ data class ChapterDownloadItem(
|
||||
_downloadState.value = ChapterDownloadState.NotDownloaded
|
||||
}
|
||||
|
||||
fun setNotDownloaded() {
|
||||
_downloadState.value = ChapterDownloadState.NotDownloaded
|
||||
}
|
||||
|
||||
fun isSelected(selectedItems: List<Long>): Boolean {
|
||||
return (chapter.id in selectedItems).also { _isSelected.value = it }
|
||||
}
|
||||
|
||||
@@ -13,12 +13,11 @@ import ca.gosyer.jui.domain.category.interactor.GetCategories
|
||||
import ca.gosyer.jui.domain.category.interactor.GetMangaCategories
|
||||
import ca.gosyer.jui.domain.category.interactor.RemoveMangaFromCategory
|
||||
import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.domain.chapter.interactor.BatchUpdateChapter
|
||||
import ca.gosyer.jui.domain.chapter.interactor.DeleteChapterDownload
|
||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapters
|
||||
import ca.gosyer.jui.domain.chapter.interactor.RefreshChapters
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterBookmarked
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMarkPreviousRead
|
||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterRead
|
||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.download.interactor.BatchChapterDownload
|
||||
import ca.gosyer.jui.domain.download.interactor.QueueChapterDownload
|
||||
@@ -60,8 +59,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
private val refreshManga: RefreshManga,
|
||||
private val getChapters: GetChapters,
|
||||
private val refreshChapters: RefreshChapters,
|
||||
private val updateChapterRead: UpdateChapterRead,
|
||||
private val updateChapterBookmarked: UpdateChapterBookmarked,
|
||||
private val batchUpdateChapter: BatchUpdateChapter,
|
||||
private val updateChapterMarkPreviousRead: UpdateChapterMarkPreviousRead,
|
||||
private val queueChapterDownload: QueueChapterDownload,
|
||||
private val stopChapterDownload: StopChapterDownload,
|
||||
@@ -229,35 +227,29 @@ class MangaScreenViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun findChapter(index: Int) = chapters.value.find { it.chapter.index == index }?.chapter
|
||||
|
||||
private fun setRead(index: Int, read: Boolean) {
|
||||
val chapter = findChapter(index) ?: return
|
||||
if (chapter.read == read) return
|
||||
private fun setRead(chapterIds: List<Long>, read: Boolean) {
|
||||
scope.launch {
|
||||
manga.value?.let { manga ->
|
||||
updateChapterRead.await(manga, index, read = read, onError = { toast(it.message.orEmpty()) })
|
||||
batchUpdateChapter.await(manga, chapterIds, isRead = read, onError = { toast(it.message.orEmpty()) })
|
||||
refreshChaptersAsync(manga.id).await()
|
||||
_selectedIds.value = _selectedIds.value.minus(chapter.id).toImmutableList()
|
||||
_selectedIds.value = persistentListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
fun markRead(index: Int) = setRead(index, true)
|
||||
fun markUnread(index: Int) = setRead(index, false)
|
||||
fun markRead(id: Long?) = setRead(listOfNotNull(id).ifEmpty { _selectedIds.value }, true)
|
||||
fun markUnread(id: Long?) = setRead(listOfNotNull(id).ifEmpty { _selectedIds.value }, false)
|
||||
|
||||
private fun setBookmarked(index: Int, bookmark: Boolean) {
|
||||
val chapter = findChapter(index) ?: return
|
||||
if (chapter.bookmarked == bookmark) return
|
||||
private fun setBookmarked(chapterIds: List<Long>, bookmark: Boolean) {
|
||||
scope.launch {
|
||||
manga.value?.let { manga ->
|
||||
updateChapterBookmarked.await(manga, index, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||
batchUpdateChapter.await(manga, chapterIds, isBookmarked = bookmark, onError = { toast(it.message.orEmpty()) })
|
||||
refreshChaptersAsync(manga.id).await()
|
||||
_selectedIds.value = _selectedIds.value.minus(chapter.id).toImmutableList()
|
||||
_selectedIds.value = persistentListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
fun bookmarkChapter(index: Int) = setBookmarked(index, true)
|
||||
fun unBookmarkChapter(index: Int) = setBookmarked(index, false)
|
||||
fun bookmarkChapter(id: Long?) = setBookmarked(listOfNotNull(id).ifEmpty { _selectedIds.value }, true)
|
||||
fun unBookmarkChapter(id: Long?) = setBookmarked(listOfNotNull(id).ifEmpty { _selectedIds.value }, false)
|
||||
|
||||
fun markPreviousRead(index: Int) {
|
||||
scope.launch {
|
||||
@@ -275,10 +267,20 @@ class MangaScreenViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteDownload(index: Int) {
|
||||
fun deleteDownload(id: Long?) {
|
||||
scope.launch {
|
||||
chapters.value.find { it.chapter.index == index }
|
||||
?.deleteDownload(deleteChapterDownload)
|
||||
if (id == null) {
|
||||
val manga = _manga.value ?: return@launch
|
||||
val chapterIds = _selectedIds.value
|
||||
batchUpdateChapter.await(manga, chapterIds, delete = true, onError = { toast(it.message.orEmpty()) })
|
||||
chapterIds.forEach { id ->
|
||||
chapters.value.find { it.chapter.id == id }?.setNotDownloaded()
|
||||
}
|
||||
_selectedIds.value = persistentListOf()
|
||||
} else {
|
||||
chapters.value.find { it.chapter.id == id }
|
||||
?.deleteDownload(deleteChapterDownload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,14 @@ fun ChapterItem(
|
||||
chapterDownload: ChapterDownloadItem,
|
||||
format: (Instant) -> String,
|
||||
onClick: (Int) -> Unit,
|
||||
markRead: (Int) -> Unit,
|
||||
markUnread: (Int) -> Unit,
|
||||
bookmarkChapter: (Int) -> Unit,
|
||||
unBookmarkChapter: (Int) -> Unit,
|
||||
markRead: (Long) -> Unit,
|
||||
markUnread: (Long) -> Unit,
|
||||
bookmarkChapter: (Long) -> Unit,
|
||||
unBookmarkChapter: (Long) -> Unit,
|
||||
markPreviousAsRead: (Int) -> Unit,
|
||||
onClickDownload: (Int) -> Unit,
|
||||
onClickStopDownload: (Int) -> Unit,
|
||||
onClickDeleteChapter: (Int) -> Unit,
|
||||
onClickDeleteChapter: (Long) -> Unit,
|
||||
onSelectChapter: (Int) -> Unit,
|
||||
onUnselectChapter: (Int) -> Unit
|
||||
) {
|
||||
@@ -79,10 +79,10 @@ fun ChapterItem(
|
||||
.selectedBackground(isSelected)
|
||||
.chapterItemModifier(
|
||||
onClick = { onClick(chapter.index) },
|
||||
markRead = { markRead(chapter.index) }.takeUnless { chapter.read },
|
||||
markUnread = { markUnread(chapter.index) }.takeIf { chapter.read },
|
||||
bookmarkChapter = { bookmarkChapter(chapter.index) }.takeUnless { chapter.bookmarked },
|
||||
unBookmarkChapter = { unBookmarkChapter(chapter.index) }.takeIf { chapter.bookmarked },
|
||||
markRead = { markRead(chapter.id) }.takeUnless { chapter.read },
|
||||
markUnread = { markUnread(chapter.id) }.takeIf { chapter.read },
|
||||
bookmarkChapter = { bookmarkChapter(chapter.id) }.takeUnless { chapter.bookmarked },
|
||||
unBookmarkChapter = { unBookmarkChapter(chapter.id) }.takeIf { chapter.bookmarked },
|
||||
markPreviousAsRead = { markPreviousAsRead(chapter.index) },
|
||||
onSelectChapter = { onSelectChapter(chapter.index) }.takeUnless { chapterDownload.isSelected.value },
|
||||
onUnselectChapter = { onUnselectChapter(chapter.index) }.takeIf { chapterDownload.isSelected.value }
|
||||
@@ -157,7 +157,7 @@ fun ChapterItem(
|
||||
chapterDownload,
|
||||
{ onClickDownload(it.index) },
|
||||
{ onClickStopDownload(it.index) },
|
||||
{ onClickDeleteChapter(it.index) }
|
||||
{ onClickDeleteChapter(it.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,13 +97,13 @@ fun MangaScreenContent(
|
||||
downloadNext: (Int) -> Unit,
|
||||
downloadUnread: () -> Unit,
|
||||
downloadAll: () -> Unit,
|
||||
markRead: (Int) -> Unit,
|
||||
markUnread: (Int) -> Unit,
|
||||
bookmarkChapter: (Int) -> Unit,
|
||||
unBookmarkChapter: (Int) -> Unit,
|
||||
markRead: (Long?) -> Unit,
|
||||
markUnread: (Long?) -> Unit,
|
||||
bookmarkChapter: (Long?) -> Unit,
|
||||
unBookmarkChapter: (Long?) -> Unit,
|
||||
markPreviousRead: (Int) -> Unit,
|
||||
downloadChapter: (Int) -> Unit,
|
||||
deleteDownload: (Int) -> Unit,
|
||||
deleteDownload: (Long?) -> Unit,
|
||||
stopDownloadingChapter: (Int) -> Unit,
|
||||
onSelectChapter: (Int) -> Unit,
|
||||
onUnselectChapter: (Int) -> Unit,
|
||||
@@ -178,12 +178,12 @@ fun MangaScreenContent(
|
||||
visible = inActionMode,
|
||||
items = getBottomActionItems(
|
||||
selectedItems = selectedItems,
|
||||
markRead = markRead,
|
||||
markUnread = markUnread,
|
||||
bookmarkChapter = bookmarkChapter,
|
||||
unBookmarkChapter = unBookmarkChapter,
|
||||
markRead = { markRead(null) },
|
||||
markUnread = { markUnread(null) },
|
||||
bookmarkChapter = { bookmarkChapter(null) },
|
||||
unBookmarkChapter = { unBookmarkChapter(null) },
|
||||
markPreviousAsRead = markPreviousRead,
|
||||
deleteChapter = deleteDownload,
|
||||
deleteChapter = { deleteDownload(null) },
|
||||
downloadChapters = downloadChapters
|
||||
)
|
||||
)
|
||||
@@ -366,35 +366,35 @@ private fun getActionModeActionItems(
|
||||
@Stable
|
||||
private fun getBottomActionItems(
|
||||
selectedItems: ImmutableList<ChapterDownloadItem>,
|
||||
markRead: (Int) -> Unit,
|
||||
markUnread: (Int) -> Unit,
|
||||
bookmarkChapter: (Int) -> Unit,
|
||||
unBookmarkChapter: (Int) -> Unit,
|
||||
markRead: () -> Unit,
|
||||
markUnread: () -> Unit,
|
||||
bookmarkChapter: () -> Unit,
|
||||
unBookmarkChapter: () -> Unit,
|
||||
markPreviousAsRead: (Int) -> Unit,
|
||||
deleteChapter: (Int) -> Unit,
|
||||
deleteChapter: () -> Unit,
|
||||
downloadChapters: () -> Unit
|
||||
): ImmutableList<BottomActionItem> {
|
||||
return listOfNotNull(
|
||||
BottomActionItem(
|
||||
name = stringResource(MR.strings.action_bookmark),
|
||||
icon = Icons.Rounded.BookmarkAdd,
|
||||
onClick = { bookmarkChapter(selectedItems.first().chapter.index) }
|
||||
).takeIf { selectedItems.fastAny { !it.chapter.bookmarked } && selectedItems.size == 1 },
|
||||
onClick = bookmarkChapter
|
||||
).takeIf { selectedItems.fastAny { !it.chapter.bookmarked } },
|
||||
BottomActionItem(
|
||||
name = stringResource(MR.strings.action_remove_bookmark),
|
||||
icon = Icons.Rounded.BookmarkRemove,
|
||||
onClick = { unBookmarkChapter(selectedItems.first().chapter.index) }
|
||||
).takeIf { selectedItems.fastAny { it.chapter.bookmarked } && selectedItems.size == 1 },
|
||||
onClick = unBookmarkChapter
|
||||
).takeIf { selectedItems.fastAny { it.chapter.bookmarked } },
|
||||
BottomActionItem(
|
||||
name = stringResource(MR.strings.action_mark_as_read),
|
||||
icon = Icons.Rounded.DoneAll,
|
||||
onClick = { markRead(selectedItems.first().chapter.index) }
|
||||
).takeIf { selectedItems.fastAny { !it.chapter.read } && selectedItems.size == 1 },
|
||||
onClick = markRead
|
||||
).takeIf { selectedItems.fastAny { !it.chapter.read } },
|
||||
BottomActionItem(
|
||||
name = stringResource(MR.strings.action_mark_as_unread),
|
||||
icon = Icons.Rounded.RemoveDone,
|
||||
onClick = { markUnread(selectedItems.first().chapter.index) }
|
||||
).takeIf { selectedItems.fastAny { it.chapter.read } && selectedItems.size == 1 },
|
||||
onClick = markUnread
|
||||
).takeIf { selectedItems.fastAny { it.chapter.read } },
|
||||
BottomActionItem(
|
||||
name = stringResource(MR.strings.action_mark_previous_read),
|
||||
icon = JuiAssets.DonePrev,
|
||||
@@ -408,7 +408,7 @@ private fun getBottomActionItems(
|
||||
BottomActionItem(
|
||||
name = stringResource(MR.strings.action_delete),
|
||||
icon = Icons.Rounded.Delete,
|
||||
onClick = { deleteChapter(selectedItems.first().chapter.index) }
|
||||
).takeIf { selectedItems.fastAny { it.downloadState.value == ChapterDownloadState.Downloaded } && selectedItems.size == 1 }
|
||||
onClick = deleteChapter
|
||||
).takeIf { selectedItems.fastAny { it.downloadState.value == ChapterDownloadState.Downloaded } }
|
||||
).toImmutableList()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user