From 478c58a9ac53df5cd8fad6b46c56b4eab110c45b Mon Sep 17 00:00:00 2001 From: Syer10 Date: Mon, 2 Sep 2024 18:08:57 -0400 Subject: [PATCH] Chapters graphql, reader is broken --- .../ca/gosyer/jui/android/ReaderActivity.kt | 10 +- data/src/commonMain/graphql/Chapters.graphql | 84 ++++++ .../ca/gosyer/jui/data/DataComponent.kt | 9 + .../jui/data/chapter/ChapterRepositoryImpl.kt | 208 ++++++++++++++ .../ca/gosyer/jui/domain/ServerListeners.kt | 44 +-- .../chapter/interactor/BatchUpdateChapter.kt | 256 ------------------ .../interactor/DeleteChapterDownload.kt | 72 +++-- .../domain/chapter/interactor/GetChapter.kt | 52 +--- .../chapter/interactor/GetChapterPage.kt | 84 ------ .../chapter/interactor/GetChapterPages.kt | 54 ++++ .../domain/chapter/interactor/GetChapters.kt | 14 +- .../chapter/interactor/RefreshChapters.kt | 8 +- .../chapter/interactor/UpdateChapter.kt | 130 +++++++++ .../interactor/UpdateChapterBookmarked.kt | 92 ------- .../interactor/UpdateChapterLastPageRead.kt | 51 +--- .../UpdateChapterMarkPreviousRead.kt | 85 ------ .../chapter/interactor/UpdateChapterMeta.kt | 11 +- .../chapter/interactor/UpdateChapterRead.kt | 51 ++-- .../jui/domain/chapter/model/Chapter.kt | 1 - .../chapter/service/ChapterRepository.kt | 56 ++++ .../domain/updates/interactor/UpdatesPager.kt | 32 +-- .../gosyer/jui/ui/reader/AndroidReaderMenu.kt | 4 +- .../jui/ui/manga/MangaScreenViewModel.kt | 24 +- .../jui/ui/manga/components/ChapterItem.kt | 4 +- .../ui/manga/components/MangaScreenContent.kt | 6 +- .../ca/gosyer/jui/ui/reader/ChapterLoader.kt | 4 +- .../ca/gosyer/jui/ui/reader/ReaderMenu.kt | 6 +- .../jui/ui/reader/ReaderMenuViewModel.kt | 120 ++++---- .../ca/gosyer/jui/ui/reader/ReaderSideMenu.kt | 29 +- .../ui/reader/loader/TachideskPageLoader.kt | 33 +-- .../gosyer/jui/ui/reader/model/ReaderPage.kt | 3 +- .../gosyer/jui/ui/reader/viewer/Continuous.kt | 6 +- .../ca/gosyer/jui/ui/reader/viewer/Pager.kt | 8 +- .../jui/ui/updates/UpdatesScreenViewModel.kt | 10 +- .../components/UpdatesScreenContent.kt | 4 +- .../gosyer/jui/ui/reader/DesktopReaderMenu.kt | 10 +- .../ca/gosyer/jui/ui/reader/IosReaderMenu.kt | 8 +- 37 files changed, 791 insertions(+), 892 deletions(-) create mode 100644 data/src/commonMain/graphql/Chapters.graphql create mode 100644 data/src/commonMain/kotlin/ca/gosyer/jui/data/chapter/ChapterRepositoryImpl.kt delete mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/BatchUpdateChapter.kt delete mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPage.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPages.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapter.kt delete mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterBookmarked.kt delete mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMarkPreviousRead.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt diff --git a/android/src/main/kotlin/ca/gosyer/jui/android/ReaderActivity.kt b/android/src/main/kotlin/ca/gosyer/jui/android/ReaderActivity.kt index e63e4ce1..ea5e835d 100644 --- a/android/src/main/kotlin/ca/gosyer/jui/android/ReaderActivity.kt +++ b/android/src/main/kotlin/ca/gosyer/jui/android/ReaderActivity.kt @@ -20,11 +20,11 @@ class ReaderActivity : AppCompatActivity() { fun newIntent( context: Context, mangaId: Long, - chapterIndex: Int, + chapterId: Long, ): Intent = Intent(context, ReaderActivity::class.java).apply { putExtra("manga", mangaId) - putExtra("chapter", chapterIndex) + putExtra("chapter", chapterId) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } } @@ -34,8 +34,8 @@ class ReaderActivity : AppCompatActivity() { val hooks = AppComponent.getInstance(applicationContext).hooks val mangaId = intent.extras!!.getLong("manga", -1) - val chapterIndex = intent.extras!!.getInt("chapter", -1) - if (mangaId == -1L || chapterIndex == -1) { + val chapterId = intent.extras!!.getLong("chapter", -1) + if (mangaId == -1L || chapterId == -1L) { finish() return } @@ -44,7 +44,7 @@ class ReaderActivity : AppCompatActivity() { CompositionLocalProvider(*hooks) { AppTheme { ReaderMenu( - chapterIndex = chapterIndex, + chapterId = chapterId, mangaId = mangaId, onCloseRequest = onBackPressedDispatcher::onBackPressed, ) diff --git a/data/src/commonMain/graphql/Chapters.graphql b/data/src/commonMain/graphql/Chapters.graphql new file mode 100644 index 00000000..39e070f4 --- /dev/null +++ b/data/src/commonMain/graphql/Chapters.graphql @@ -0,0 +1,84 @@ +fragment ChapterFragment on ChapterType { + chapterNumber + fetchedAt + id + isBookmarked + isDownloaded + isRead + lastPageRead + lastReadAt + mangaId + name + pageCount + realUrl + scanlator + sourceOrder + uploadDate + url + meta { + key + value + } +} + +query GetChapter($id: Int!) { + chapter(id: $id) { + ...ChapterFragment + } +} + +query GetMangaChapters($id: Int!) { + chapters( + condition: {mangaId: $id} + orderBy: SOURCE_ORDER, + orderByType: DESC, + ) { + nodes { + ...ChapterFragment + } + } +} + +mutation UpdateChapter($id: Int!, $patch: UpdateChapterPatchInput!) { + updateChapter(input: {id: $id, patch: $patch}) { + clientMutationId + } +} + +mutation UpdateChapters($id: [Int!]!, $patch: UpdateChapterPatchInput!) { + updateChapters(input: {ids: $id, patch: $patch}) { + clientMutationId + } +} + +mutation UpdateChapterMeta($id: Int!, $key: String!, $value: String!) { + setChapterMeta(input: {meta: {chapterId: $id, key: $key, value: $value}}) { + clientMutationId + } +} + +mutation DeleteDownloadedChapter($id: Int!) { + deleteDownloadedChapter(input: {id: $id}) { + clientMutationId + } +} + +mutation DeleteDownloadedChapters($ids: [Int!]!) { + deleteDownloadedChapters(input: {ids: $ids}) { + clientMutationId + } +} + +mutation FetchChapters($mangaId: Int!) { + fetchChapters(input: {mangaId: $mangaId}) { + chapters { + ...ChapterFragment + } + } +} + +mutation FetchChapterPages($id: Int!) { + fetchChapterPages(input: {chapterId: $id}) { + pages + } +} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt index 8b4310c7..184c6a76 100644 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt +++ b/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt @@ -7,7 +7,9 @@ package ca.gosyer.jui.data import ca.gosyer.jui.core.lang.addSuffix +import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl import ca.gosyer.jui.data.settings.SettingsRepositoryImpl +import ca.gosyer.jui.domain.chapter.service.ChapterRepository import ca.gosyer.jui.domain.server.Http import ca.gosyer.jui.domain.server.service.ServerPreferences import ca.gosyer.jui.domain.settings.service.SettingsRepository @@ -50,4 +52,11 @@ interface DataComponent : SharedDataComponent { @Provides fun settingsRepository(apolloClient: ApolloClient): SettingsRepository = SettingsRepositoryImpl(apolloClient) + + @Provides + fun chapterRepository( + apolloClient: ApolloClient, + http: Http, + serverPreferences: ServerPreferences, + ): ChapterRepository = ChapterRepositoryImpl(apolloClient, http, serverPreferences.serverUrl().get()) } diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/chapter/ChapterRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/chapter/ChapterRepositoryImpl.kt new file mode 100644 index 00000000..b3497377 --- /dev/null +++ b/data/src/commonMain/kotlin/ca/gosyer/jui/data/chapter/ChapterRepositoryImpl.kt @@ -0,0 +1,208 @@ +package ca.gosyer.jui.data.chapter + +import ca.gosyer.jui.data.graphql.DeleteDownloadedChapterMutation +import ca.gosyer.jui.data.graphql.DeleteDownloadedChaptersMutation +import ca.gosyer.jui.data.graphql.FetchChapterPagesMutation +import ca.gosyer.jui.data.graphql.FetchChaptersMutation +import ca.gosyer.jui.data.graphql.GetChapterQuery +import ca.gosyer.jui.data.graphql.GetMangaChaptersQuery +import ca.gosyer.jui.data.graphql.UpdateChapterMetaMutation +import ca.gosyer.jui.data.graphql.UpdateChapterMutation +import ca.gosyer.jui.data.graphql.UpdateChaptersMutation +import ca.gosyer.jui.data.graphql.fragment.ChapterFragment +import ca.gosyer.jui.data.graphql.type.UpdateChapterPatchInput +import ca.gosyer.jui.domain.chapter.model.Chapter +import ca.gosyer.jui.domain.chapter.model.ChapterMeta +import ca.gosyer.jui.domain.chapter.service.ChapterRepository +import ca.gosyer.jui.domain.server.Http +import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.api.Optional +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.get +import io.ktor.client.statement.readBytes +import io.ktor.http.Url +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map + +class ChapterRepositoryImpl( + private val apolloClient: ApolloClient, + private val http: Http, + private val serverUrl: Url, +) : ChapterRepository { + override fun getChapter(chapterId: Long): Flow { + return apolloClient.query( + GetChapterQuery(chapterId.toInt()) + ) + .toFlow() + .map { + val data = it.dataAssertNoErrors + data.chapter.chapterFragment.toChapter() + } + } + + override fun getChapters(mangaId: Long): Flow> { + return apolloClient.query( + GetMangaChaptersQuery(mangaId.toInt()) + ) + .toFlow() + .map { + val data = it.dataAssertNoErrors + data.chapters.nodes.map { it.chapterFragment.toChapter() } + } + } + + override fun updateChapter( + chapterId: Long, + bookmarked: Boolean?, + read: Boolean?, + lastPageRead: Int?, + ): Flow { + return apolloClient.mutation( + UpdateChapterMutation( + chapterId.toInt(), + UpdateChapterPatchInput( + isBookmarked = Optional.presentIfNotNull(bookmarked), + isRead = Optional.presentIfNotNull(read), + lastPageRead = Optional.presentIfNotNull(lastPageRead) + ), + ) + ) + .toFlow() + .map { + it.dataAssertNoErrors + } + } + + override fun updateChapters( + chapterIds: List, + bookmarked: Boolean?, + read: Boolean?, + lastPageRead: Int?, + ): Flow { + return apolloClient.mutation( + UpdateChaptersMutation( + chapterIds.map { it.toInt() }, + UpdateChapterPatchInput( + isBookmarked = Optional.presentIfNotNull(bookmarked), + isRead = Optional.presentIfNotNull(read), + lastPageRead = Optional.presentIfNotNull(lastPageRead) + ), + ) + ) + .toFlow() + .map { + it.dataAssertNoErrors + } + } + + override fun deleteDownloadedChapter( + chapterId: Long, + ): Flow { + return apolloClient.mutation( + DeleteDownloadedChapterMutation( + chapterId.toInt(), + ) + ) + .toFlow() + .map { + it.dataAssertNoErrors + } + } + + override fun deleteDownloadedChapters( + chapterIds: List, + ): Flow { + return apolloClient.mutation( + DeleteDownloadedChaptersMutation( + chapterIds.map { it.toInt() }, + ) + ) + .toFlow() + .map { + it.dataAssertNoErrors + } + } + + override fun updateChapterMeta( + chapterId: Long, + key: String, + value: String, + ): Flow { + return apolloClient.mutation( + UpdateChapterMetaMutation( + chapterId.toInt(), + key, + value, + ) + ) + .toFlow() + .map { + it.dataAssertNoErrors + } + } + + override fun fetchChapters( + mangaId: Long, + ): Flow> { + return apolloClient.mutation( + FetchChaptersMutation( + mangaId.toInt(), + ) + ) + .toFlow() + .map { + val chapters = it.dataAssertNoErrors + chapters.fetchChapters.chapters.map { it.chapterFragment.toChapter() } + } + } + + override fun getPages(chapterId: Long): Flow> { + return apolloClient.mutation( + FetchChapterPagesMutation( + chapterId.toInt(), + ) + ) + .toFlow() + .map { + val chapters = it.dataAssertNoErrors + chapters.fetchChapterPages.pages + } + } + + override fun getPage( + url: String, + block: HttpRequestBuilder.() -> Unit, + ): Flow { + val realUrl = Url("$serverUrl$url") + + return flow { http.get(realUrl, block).readBytes() } + } + + + companion object { + internal fun ChapterFragment.toChapter(): Chapter { + return Chapter( + id = id.toLong(), + url = url, + name = name, + uploadDate = uploadDate, + chapterNumber = chapterNumber.toFloat(), + scanlator = scanlator, + mangaId = mangaId.toLong(), + read = isRead, + bookmarked = isBookmarked, + lastPageRead = lastPageRead, + index = sourceOrder, + fetchedAt = fetchedAt, + realUrl = realUrl, + pageCount = pageCount, + lastReadAt = lastPageRead, + downloaded = isDownloaded, + meta = ChapterMeta( + juiPageOffset = meta.find { it.key == "juiPageOffset" }?.value?.toIntOrNull() ?: 0 + ) + ) + } + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/ServerListeners.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/ServerListeners.kt index 6ac77625..0a0cbbfe 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/ServerListeners.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/ServerListeners.kt @@ -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?>>( - extraBufferCapacity = Channel.UNLIMITED, - ) - val chapterIndexesListener = _chapterIndexesListener.asSharedFlow() - - private val _chapterIdsListener = MutableSharedFlow>>( + private val _chapterIdsListener = MutableSharedFlow>( extraBufferCapacity = Channel.UNLIMITED, ) val chapterIdsListener = _chapterIdsListener.asSharedFlow() @@ -91,58 +85,32 @@ class ServerListeners fun combineChapters( flow: Flow, - indexPredate: (suspend (Long, List?) -> Boolean)? = null, - idPredate: (suspend (Long?, List) -> Boolean)? = null, + idPredate: (suspend (List) -> Boolean)? = null, ): Flow { - 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, - ) { - 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, ) { 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()) } } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/BatchUpdateChapter.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/BatchUpdateChapter.kt deleted file mode 100644 index 0528e3aa..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/BatchUpdateChapter.kt +++ /dev/null @@ -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, - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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() - } - } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/DeleteChapterDownload.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/DeleteChapterDownload.kt index 0bfdf0bd..f0537466 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/DeleteChapterDownload.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/DeleteChapterDownload.kt @@ -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, + 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, + 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, + ) = chapterRepository.deleteDownloadedChapters(chapterIds) + .onEach { serverListeners.updateChapters(chapterIds) } + + @JvmName("asFlowChapters") + fun asFlow(chapter: List) = + chapterRepository.deleteDownloadedChapters(chapter.map { it.id }) + .onEach { serverListeners.updateChapters(chapter.map { it.id }) } companion object { private val log = logging() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapter.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapter.kt index 217d8c3a..e14f3a53 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapter.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapter.kt @@ -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 { diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPage.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPage.kt deleted file mode 100644 index db982cb9..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPage.kt +++ /dev/null @@ -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() - } - } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPages.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPages.kt new file mode 100644 index 00000000..88867aa8 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPages.kt @@ -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() + } + } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapters.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapters.kt index b91fc9c3..4d7a5cc3 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapters.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapters.kt @@ -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 { diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/RefreshChapters.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/RefreshChapters.kt index 3aaaf90a..3af2091f 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/RefreshChapters.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/RefreshChapters.kt @@ -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 { diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapter.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapter.kt new file mode 100644 index 00000000..b4143ced --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapter.kt @@ -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, + 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, + 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, + 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, + 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() + } + } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterBookmarked.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterBookmarked.kt deleted file mode 100644 index e507ecd1..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterBookmarked.kt +++ /dev/null @@ -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() - } - } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterLastPageRead.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterLastPageRead.kt index 26976ace..2ba09753 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterLastPageRead.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterLastPageRead.kt @@ -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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMarkPreviousRead.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMarkPreviousRead.kt deleted file mode 100644 index 358de164..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMarkPreviousRead.kt +++ /dev/null @@ -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() - } - } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMeta.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMeta.kt index a68bce03..62da46ae 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMeta.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMeta.kt @@ -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) } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterRead.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterRead.kt index 705fe91f..c42faad8 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterRead.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterRead.kt @@ -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, 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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/model/Chapter.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/model/Chapter.kt index f2fd7b13..9a941371 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/model/Chapter.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/model/Chapter.kt @@ -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, diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt new file mode 100644 index 00000000..139e45f4 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt @@ -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 + + fun getChapters( + mangaId: Long, + ): Flow> + + fun updateChapter( + chapterId: Long, + bookmarked: Boolean? = null, + read: Boolean? = null, + lastPageRead: Int? = null, + ): Flow + + fun updateChapters( + chapterIds: List, + bookmarked: Boolean? = null, + read: Boolean? = null, + lastPageRead: Int? = null, + ): Flow + + fun deleteDownloadedChapter( + chapterId: Long, + ): Flow + + fun deleteDownloadedChapters( + chapterIds: List, + ): Flow + + fun updateChapterMeta( + chapterId: Long, + key: String, + value: String, + ): Flow + + fun fetchChapters( + mangaId: Long, + ): Flow> + + fun getPages( + chapterId: Long, + ): Flow> + + fun getPage( + url: String, + block: HttpRequestBuilder.() -> Unit, + ): Flow +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/interactor/UpdatesPager.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/interactor/UpdatesPager.kt index 0afed9a9..16d87f43 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/interactor/UpdatesPager.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/interactor/UpdatesPager.kt @@ -81,7 +81,7 @@ class UpdatesPager updates.filterIsInstance().map { it.manga.id } }.stateIn(this, SharingStarted.Eagerly, emptyList()) private val chapterIds = foldedUpdates.map { updates -> - updates.filterIsInstance().map { Triple(it.manga.id, it.chapter.index, it.chapter.id) } + updates.filterIsInstance().map { it.chapter.id } }.stateIn(this, SharingStarted.Eagerly, emptyList()) private val changedManga = serverListeners.mangaListener.runningFold(emptyMap()) { manga, updatedMangaIds -> @@ -97,36 +97,12 @@ class UpdatesPager private val changedChapters = MutableStateFlow(emptyMap()) init { - serverListeners.chapterIndexesListener - .onEach { (mangaId, chapterIndexes) -> - if (chapterIndexes == null) { - val chapters = coroutineScope { - foldedUpdates.value.filterIsInstance().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 } } diff --git a/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/reader/AndroidReaderMenu.kt b/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/reader/AndroidReaderMenu.kt index 658eb859..47070088 100644 --- a/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/reader/AndroidReaderMenu.kt +++ b/presentation/src/androidMain/kotlin/ca/gosyer/jui/ui/reader/AndroidReaderMenu.kt @@ -16,12 +16,12 @@ actual class ReaderLauncher( private val context: Context, ) { actual fun launch( - chapterIndex: Int, + chapterId: Long, mangaId: Long, ) { Intent(context, Class.forName("ca.gosyer.jui.android.ReaderActivity")).apply { putExtra("manga", mangaId) - putExtra("chapter", chapterIndex) + putExtra("chapter", chapterId) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) }.let(context::startActivity) } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt index e4065c91..ebd46fae 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt @@ -12,11 +12,10 @@ 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.UpdateChapterMarkPreviousRead +import ca.gosyer.jui.domain.chapter.interactor.UpdateChapter import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.download.interactor.BatchChapterDownload import ca.gosyer.jui.domain.download.interactor.QueueChapterDownload @@ -63,8 +62,7 @@ class MangaScreenViewModel private val refreshManga: RefreshManga, private val getChapters: GetChapters, private val refreshChapters: RefreshChapters, - private val batchUpdateChapter: BatchUpdateChapter, - private val updateChapterMarkPreviousRead: UpdateChapterMarkPreviousRead, + private val updateChapter: UpdateChapter, private val queueChapterDownload: QueueChapterDownload, private val stopChapterDownload: StopChapterDownload, private val deleteChapterDownload: DeleteChapterDownload, @@ -253,8 +251,8 @@ class MangaScreenViewModel read: Boolean, ) { scope.launch { - manga.value?.let { manga -> - batchUpdateChapter.await(manga, chapterIds, isRead = read, onError = { toast(it.message.orEmpty()) }) + manga.value?.let { + updateChapter.await(chapterIds, read = read, onError = { toast(it.message.orEmpty()) }) _selectedIds.value = persistentListOf() } } @@ -269,8 +267,8 @@ class MangaScreenViewModel bookmark: Boolean, ) { scope.launch { - manga.value?.let { manga -> - batchUpdateChapter.await(manga, chapterIds, isBookmarked = bookmark, onError = { toast(it.message.orEmpty()) }) + manga.value?.let { + updateChapter.await(chapterIds, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) }) _selectedIds.value = persistentListOf() } } @@ -282,8 +280,11 @@ class MangaScreenViewModel fun markPreviousRead(index: Int) { scope.launch { - manga.value?.let { manga -> - updateChapterMarkPreviousRead.await(manga, index, onError = { toast(it.message.orEmpty()) }) + manga.value?.let { + val chapters = chapters.value + .sortedBy { it.chapter.index } + .subList(0, index).map{it.chapter.id} // todo test + updateChapter.await(chapters, read = true, onError = { toast(it.message.orEmpty()) }) _selectedIds.value = persistentListOf() } } @@ -298,9 +299,8 @@ class MangaScreenViewModel fun deleteDownload(id: Long?) { scope.launch { if (id == null) { - val manga = _manga.value ?: return@launch val chapterIds = _selectedIds.value - batchUpdateChapter.await(manga, chapterIds, delete = true, onError = { toast(it.message.orEmpty()) }) + deleteChapterDownload.await(chapterIds, onError = { toast(it.message.orEmpty()) }) selectedItems.value.forEach { it.setNotDownloaded() } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/ChapterItem.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/ChapterItem.kt index a792bae9..9a031e0f 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/ChapterItem.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/ChapterItem.kt @@ -58,7 +58,7 @@ expect fun Modifier.chapterItemModifier( fun ChapterItem( chapterDownload: ChapterDownloadItem, format: (Instant) -> String, - onClick: (Int) -> Unit, + onClick: (Long) -> Unit, markRead: (Long) -> Unit, markUnread: (Long) -> Unit, bookmarkChapter: (Long) -> Unit, @@ -78,7 +78,7 @@ fun ChapterItem( .height(70.dp) .selectedBackground(isSelected) .chapterItemModifier( - onClick = { onClick(chapter.index) }, + onClick = { onClick(chapter.id) }, markRead = { markRead(chapter.id) }.takeUnless { chapter.read }, markUnread = { markUnread(chapter.id) }.takeIf { chapter.read }, bookmarkChapter = { bookmarkChapter(chapter.id) }.takeUnless { chapter.bookmarked }, diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaScreenContent.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaScreenContent.kt index 5f9bad2f..d085e7dd 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaScreenContent.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaScreenContent.kt @@ -228,11 +228,9 @@ fun MangaScreenContent( onClick = if (inActionMode) { { if (chapter.isSelected.value) { - onUnselectChapter( - chapter.chapter.id, - ) + onUnselectChapter(it) } else { - onSelectChapter(chapter.chapter.id) + onSelectChapter(it) } } } else { diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ChapterLoader.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ChapterLoader.kt index 1e620f2c..0149b4a3 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ChapterLoader.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ChapterLoader.kt @@ -6,6 +6,7 @@ package ca.gosyer.jui.ui.reader +import ca.gosyer.jui.domain.chapter.interactor.GetChapterPages import ca.gosyer.jui.domain.reader.service.ReaderPreferences import ca.gosyer.jui.domain.server.Http import ca.gosyer.jui.ui.base.image.BitmapDecoderFactory @@ -26,6 +27,7 @@ class ChapterLoader( private val http: Http, private val chapterCache: DiskCache, private val bitmapDecoderFactory: BitmapDecoderFactory, + private val getChapterPages: GetChapterPages, ) { fun loadChapter(chapter: ReaderChapter): StateFlow { if (chapterIsReady(chapter)) { @@ -34,7 +36,7 @@ class ChapterLoader( chapter.state = ReaderChapter.State.Loading log.debug { "Loading pages for ${chapter.chapter.name}" } - val loader = TachideskPageLoader(chapter, readerPreferences, http, chapterCache, bitmapDecoderFactory) + val loader = TachideskPageLoader(chapter, readerPreferences, http, chapterCache, bitmapDecoderFactory, getChapterPages) val pages = loader.getPages() diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt index f322ef34..846e98af 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenu.kt @@ -101,7 +101,7 @@ import kotlinx.coroutines.launch expect class ReaderLauncher { fun launch( - chapterIndex: Int, + chapterId: Long, mangaId: Long, ) @@ -114,12 +114,12 @@ expect fun rememberReaderLauncher(): ReaderLauncher @Composable fun ReaderMenu( - chapterIndex: Int, + chapterId: Long, mangaId: Long, onCloseRequest: () -> Unit, ) { val viewModels = LocalViewModels.current - val vm = remember { viewModels.readerViewModel(ReaderMenuViewModel.Params(chapterIndex, mangaId)) } + val vm = remember { viewModels.readerViewModel(ReaderMenuViewModel.Params(chapterId, mangaId)) } DisposableEffect(vm) { onDispose(vm::onDispose) } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt index e9d73a77..ecc9f129 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt @@ -9,11 +9,10 @@ package ca.gosyer.jui.ui.reader import ca.gosyer.jui.core.lang.launchDefault import ca.gosyer.jui.core.prefs.getAsFlow import ca.gosyer.jui.domain.chapter.interactor.GetChapter -import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage +import ca.gosyer.jui.domain.chapter.interactor.GetChapterPages import ca.gosyer.jui.domain.chapter.interactor.GetChapters -import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterLastPageRead +import ca.gosyer.jui.domain.chapter.interactor.UpdateChapter import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMeta -import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterRead import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.manga.interactor.GetManga import ca.gosyer.jui.domain.manga.interactor.UpdateMangaMeta @@ -77,9 +76,8 @@ class ReaderMenuViewModel private val getManga: GetManga, private val getChapters: GetChapters, private val getChapter: GetChapter, - private val getChapterPage: GetChapterPage, - private val updateChapterRead: UpdateChapterRead, - private val updateChapterLastPageRead: UpdateChapterLastPageRead, + private val getChapterPages: GetChapterPages, + private val updateChapter: UpdateChapter, private val updateMangaMeta: UpdateMangaMeta, private val updateChapterMeta: UpdateChapterMeta, private val chapterCache: ChapterCache, @@ -157,6 +155,7 @@ class ReaderMenuViewModel http = http, chapterCache = chapterCache, bitmapDecoderFactory = BitmapDecoderFactory(contextWrapper), + getChapterPages = getChapterPages, ) init { @@ -167,7 +166,7 @@ class ReaderMenuViewModel scope.launchDefault { runCatching { initManga(params.mangaId) - initChapters(params.mangaId, params.chapterIndex) + initChapters(params.mangaId, params.chapterId) } } } @@ -179,11 +178,13 @@ class ReaderMenuViewModel .collectLatest { page -> page.chapter.pageLoader?.loadPage(page) if (page.chapter == chapter.value) { - if ((page.index + 1) >= page.chapter.chapter.pageCount!!) { + val pages = page.chapter.pages.value as? PagesState.Success + ?: return@collectLatest + if ((page.index2 + 1) >= pages.pages.size) { markChapterRead(page.chapter) } val nextChapter = nextChapter.value - if (nextChapter != null && (page.index + 1) >= (page.chapter.chapter.pageCount!! - 5)) { + if (nextChapter != null && (page.index2 + 1) >= ((pages.pages.size - 5).coerceAtLeast(1))) { requestPreloadChapter(nextChapter) } } else { @@ -191,10 +192,10 @@ class ReaderMenuViewModel val nextChapter = nextChapter.value if (page.chapter == previousChapter) { viewerChapters.value = viewerChapters.value.movePrev() - initChapters(params.mangaId, page.chapter.chapter.index, fromMenuButton = false) + initChapters(params.mangaId, page.chapter.chapter.id, fromMenuButton = false) } else if (page.chapter == nextChapter) { viewerChapters.value = viewerChapters.value.moveNext() - initChapters(params.mangaId, page.chapter.chapter.index, fromMenuButton = false) + initChapters(params.mangaId, page.chapter.chapter.id, fromMenuButton = false) } } } @@ -253,7 +254,7 @@ class ReaderMenuViewModel } fun retry(page: ReaderPage) { - log.info { "Retrying ${page.index}" } + log.info { "Retrying ${page.index2}" } chapter.value?.pageLoader?.retryPage(page) } @@ -277,7 +278,7 @@ class ReaderMenuViewModel _state.value = ReaderChapter.State.Wait sendProgress() viewerChapters.value = viewerChapters.value.movePrev() - initChapters(params.mangaId, prevChapter.chapter.index, fromMenuButton = true) + initChapters(params.mangaId, prevChapter.chapter.id, fromMenuButton = true) } catch (e: Exception) { log.warn(e) { "Error loading prev chapter" } } @@ -291,7 +292,7 @@ class ReaderMenuViewModel _state.value = ReaderChapter.State.Wait sendProgress() viewerChapters.value = viewerChapters.value.moveNext() - initChapters(params.mangaId, nextChapter.chapter.index, fromMenuButton = true) + initChapters(params.mangaId, nextChapter.chapter.id, fromMenuButton = true) } catch (e: Exception) { log.warn(e) { "Error loading next chapter" } } @@ -313,52 +314,45 @@ class ReaderMenuViewModel private suspend fun initChapters( mangaId: Long, - chapterIndex: Int, + chapterId: Long, fromMenuButton: Boolean = true, ) { - log.debug { "Loading chapter index $chapterIndex" } + log.debug { "Loading chapter index $chapterId" } val (chapter, pages) = coroutineScope { - val getCurrentChapter = async { - val chapter = getReaderChapter(chapterIndex) ?: return@async null - val pages = loader.loadChapter(chapter) - viewerChapters.update { it.copy(currChapter = chapter) } - chapter to pages + val chapters = getChapters.asFlow(mangaId) + .take(1) + .catch { + _state.value = ReaderChapter.State.Error(it) + log.warn(it) { "Error getting chapters for $mangaId" } + } + .singleOrNull() + ?: return@coroutineScope null + val chapter = chapters.find { it.id == chapterId } + ?.let { ReaderChapter(it) } + ?: return@coroutineScope null + val pages = loader.loadChapter(chapter) + viewerChapters.update { it.copy(currChapter = chapter) } + + if (viewerChapters.value.nextChapter == null) { + val nextChapter = chapters.find { it.index == chapter.chapter.index + 1 } + if (nextChapter != null) { + val nextReaderChapter = ReaderChapter(nextChapter) + viewerChapters.update { it.copy(nextChapter = nextReaderChapter) } + } else { + viewerChapters.update { it.copy(nextChapter = null) } + } } - val getAdjacentChapters = async { - val chapters = getChapters.await( - mangaId, - onError = { /* TODO: 2022-07-01 Error toast */ }, - ).orEmpty() - - val nextChapter = async { - if (viewerChapters.value.nextChapter == null) { - val nextChapter = chapters.find { it.index == chapterIndex + 1 } - if (nextChapter != null) { - val nextReaderChapter = getReaderChapter(nextChapter.index) - viewerChapters.update { it.copy(nextChapter = nextReaderChapter) } - } else { - viewerChapters.update { it.copy(nextChapter = null) } - } - } + if (viewerChapters.value.prevChapter == null) { + val prevChapter = chapters.find { it.index == chapter.chapter.index - 1 } + if (prevChapter != null) { + val prevReaderChapter = ReaderChapter(prevChapter) + viewerChapters.update { it.copy(prevChapter = prevReaderChapter) } + } else { + viewerChapters.update { it.copy(prevChapter = null) } } - val prevChapter = async { - if (viewerChapters.value.prevChapter == null) { - val prevChapter = chapters.find { it.index == chapterIndex - 1 } - if (prevChapter != null) { - val prevReaderChapter = getReaderChapter(prevChapter.index) - viewerChapters.update { it.copy(prevChapter = prevReaderChapter) } - } else { - viewerChapters.update { it.copy(prevChapter = null) } - } - } - } - nextChapter.await() - prevChapter.await() } - - getAdjacentChapters.await() - getCurrentChapter.await() + chapter to pages } ?: return if (fromMenuButton) { @@ -386,18 +380,6 @@ class ReaderMenuViewModel } } - private suspend fun getReaderChapter(chapterIndex: Int): ReaderChapter? { - return ReaderChapter( - getChapter.asFlow(params.mangaId, chapterIndex) - .take(1) - .catch { - _state.value = ReaderChapter.State.Error(it) - log.warn(it) { "Error getting chapter $chapterIndex" } - } - .singleOrNull() ?: return null, - ) - } - fun requestPreloadChapter(chapter: ReaderChapter) { if (chapter.state != ReaderChapter.State.Wait && chapter.state !is ReaderChapter.State.Error) { return @@ -408,19 +390,19 @@ class ReaderMenuViewModel private fun markChapterRead(chapter: ReaderChapter) { scope.launch { - updateChapterRead.await(chapter.chapter, read = true, onError = { toast(it.message.orEmpty()) }) + updateChapter.await(chapter.chapter, read = true, onError = { toast(it.message.orEmpty()) }) } } @OptIn(DelicateCoroutinesApi::class) fun sendProgress( chapter: Chapter? = this.chapter.value?.chapter, - lastPageRead: Int = (currentPage.value as? ReaderPage)?.index ?: 0, + lastPageRead: Int = (currentPage.value as? ReaderPage)?.index2 ?: 0, ) { chapter ?: return if (chapter.read) return GlobalScope.launch { - updateChapterLastPageRead.await( + updateChapter.await( chapter, lastPageRead = lastPageRead, onError = { toast(it.message.orEmpty()) }, @@ -448,7 +430,7 @@ class ReaderMenuViewModel } data class Params( - val chapterIndex: Int, + val chapterId: Long, val mangaId: Long, ) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt index 37a4a78c..43197ea8 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderSideMenu.kt @@ -42,7 +42,9 @@ import androidx.compose.material.icons.rounded.ChevronLeft import androidx.compose.material.icons.rounded.SkipNext import androidx.compose.material.icons.rounded.SkipPrevious import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -56,6 +58,7 @@ import ca.gosyer.jui.core.util.replace import ca.gosyer.jui.domain.manga.model.MangaMeta import ca.gosyer.jui.domain.reader.model.Direction import ca.gosyer.jui.i18n.MR +import ca.gosyer.jui.ui.reader.loader.PagesState import ca.gosyer.jui.ui.reader.model.ReaderChapter import ca.gosyer.jui.ui.reader.model.ReaderItem import ca.gosyer.jui.uicore.components.AroundLayout @@ -82,7 +85,6 @@ fun ReaderSideMenu( ) { Surface(Modifier.fillMaxHeight().width(260.dp)) { Column(Modifier.fillMaxSize()) { - val pageCount = chapter.chapter.pageCount!! ReaderMenuToolbar(onCloseSideMenuClicked = onCloseSideMenuClicked) Spacer(Modifier.height(4.dp)) ReaderModeSetting( @@ -91,6 +93,12 @@ fun ReaderSideMenu( onSetReaderMode = onSetReaderMode, ) Spacer(Modifier.height(4.dp)) + val chapterPages by key(chapter.chapter.id) { chapter.pages.collectAsState() } + val pageCount = when (chapterPages) { + PagesState.Empty -> null + PagesState.Loading -> null + is PagesState.Success -> (chapterPages as? PagesState.Success)?.pages?.size + } ReaderProgressSlider( pages = pages, currentPage = currentPage, @@ -183,6 +191,12 @@ fun ReaderExpandBottomMenu( shape = CircleShape, backgroundColor = MaterialTheme.colors.surface.copy(alpha = 0.5F), ) { + val chapterPages by key(chapter.chapter.id) { chapter.pages.collectAsState() } + val pageCount = when (chapterPages) { + PagesState.Empty -> null + PagesState.Loading -> null + is PagesState.Success -> (chapterPages as? PagesState.Success)?.pages?.size + } AroundLayout( Modifier.padding(horizontal = 8.dp), startLayout = { @@ -190,7 +204,7 @@ fun ReaderExpandBottomMenu( val text = if (!isRtL) { pages.indexOf(currentPage) } else { - chapter.chapter.pageCount!! + pageCount ?: "1" }.toString() Text(text, fontSize = 15.sp) } @@ -200,7 +214,7 @@ fun ReaderExpandBottomMenu( val text = if (isRtL) { pages.indexOf(currentPage) } else { - chapter.chapter.pageCount!! + pageCount ?: "1" }.toString() Text(text, fontSize = 15.sp) } @@ -212,7 +226,7 @@ fun ReaderExpandBottomMenu( .padding(horizontal = 4.dp), pages = pages, currentPage = currentPage, - pageCount = chapter.chapter.pageCount!!, + pageCount = pageCount, onNewPageClicked = navigate, isRtL = isRtL, ) @@ -278,7 +292,7 @@ private fun ReaderProgressSlider( modifier: Modifier = Modifier, pages: ImmutableList, currentPage: ReaderItem?, - pageCount: Int, + pageCount: Int?, onNewPageClicked: (Int) -> Unit, isRtL: Boolean, ) { @@ -295,9 +309,10 @@ private fun ReaderProgressSlider( onNewPageClicked(it.roundToInt()) } }, - valueRange = 0F..pageCount.toFloat(), - steps = pageCount, + valueRange = 0F..(pageCount ?: 1).toFloat(), + steps = (pageCount ?: 1), onValueChangeFinished = { isValueChanging = false }, + enabled = pageCount != null, modifier = modifier.let { if (isRtL) { it then Modifier.rotate(180F) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt index 109adb05..04780f09 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/loader/TachideskPageLoader.kt @@ -8,15 +8,16 @@ package ca.gosyer.jui.ui.reader.loader import androidx.compose.ui.unit.IntSize import ca.gosyer.jui.core.io.SYSTEM +import ca.gosyer.jui.core.io.source import ca.gosyer.jui.core.lang.PriorityChannel import ca.gosyer.jui.core.lang.throwIfCancellation +import ca.gosyer.jui.domain.chapter.interactor.GetChapterPages import ca.gosyer.jui.domain.reader.service.ReaderPreferences import ca.gosyer.jui.domain.server.Http import ca.gosyer.jui.ui.base.image.BitmapDecoderFactory import ca.gosyer.jui.ui.base.model.StableHolder import ca.gosyer.jui.ui.reader.model.ReaderChapter import ca.gosyer.jui.ui.reader.model.ReaderPage -import ca.gosyer.jui.ui.util.lang.toSource import cafe.adriel.voyager.core.concurrent.AtomicInt32 import com.seiko.imageloader.asImageBitmap import com.seiko.imageloader.cache.disk.DiskCache @@ -25,9 +26,6 @@ import com.seiko.imageloader.model.DataSource import com.seiko.imageloader.model.ImageResult import com.seiko.imageloader.option.Options import io.ktor.client.plugins.onDownload -import io.ktor.client.request.get -import io.ktor.client.statement.HttpResponse -import io.ktor.client.statement.bodyAsChannel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO @@ -39,6 +37,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn @@ -57,6 +56,7 @@ class TachideskPageLoader( private val http: Http, private val chapterCache: DiskCache, private val bitmapDecoderFactory: BitmapDecoderFactory, + private val getChapterPages: GetChapterPages, ) : PageLoader() { val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) @@ -109,15 +109,15 @@ class TachideskPageLoader( } private suspend fun fetchImage(page: ReaderPage) { - log.debug { "Loading page ${page.index}" } + log.debug { "Loading page ${page.index2}" } flow { - val response = http.get("api/v1/manga/${chapter.chapter.mangaId}/chapter/${chapter.chapter.index}/page/${page.index}") { + val response = getChapterPages.asFlow(page.url) { onDownload { bytesSentTotal, contentLength -> page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F) } } - emit(response) + emitAll(response) } .onEach { putImageInCache(it, page) @@ -129,21 +129,21 @@ class TachideskPageLoader( page.bitmap.value = StableHolder(null) page.status.value = ReaderPage.Status.ERROR page.error.value = it.message - log.warn(it) { "Failed to get page ${page.index} for chapter ${chapter.chapter.index} for ${chapter.chapter.mangaId}" } + log.warn(it) { "Failed to get page ${page.index2} for chapter ${chapter.chapter.index} for ${chapter.chapter.mangaId}" } } .flowOn(Dispatchers.IO) .collect() } private suspend fun putImageInCache( - response: HttpResponse, + response: ByteArray, page: ReaderPage, ) { val editor = chapterCache.openEditor(page.cacheKey) ?: throw Exception("Couldn't open cache") try { FileSystem.SYSTEM.write(editor.data) { - response.bodyAsChannel().toSource().use { + response.source().use { writeAll(it) } } @@ -192,7 +192,7 @@ class TachideskPageLoader( currentPage: ReaderPage, amount: Int, ): List { - val pageIndex = currentPage.index + val pageIndex = currentPage.index2 val pages = (currentPage.chapter.pages.value as? PagesState.Success)?.pages ?: return emptyList() if (pageIndex >= pages.lastIndex) return emptyList() @@ -214,14 +214,15 @@ class TachideskPageLoader( override fun getPages(): StateFlow { scope.launch { if (pagesFlow.value != PagesState.Loading) return@launch - val pageRange = chapter.chapter.pageCount?.let { 0..it.minus(1) } - pagesFlow.value = if (pageRange == null || pageRange.isEmpty()) { + val pages = getChapterPages.await(chapter.chapter.id) + pagesFlow.value = if (pages.isNullOrEmpty()) { PagesState.Empty } else { PagesState.Success( - pageRange.map { + pages.mapIndexed { index, url -> ReaderPage( - index = it, + url = url, + index2 = index, bitmap = MutableStateFlow(StableHolder(null)), bitmapInfo = MutableStateFlow(null), progress = MutableStateFlow(0.0F), @@ -295,7 +296,7 @@ class TachideskPageLoader( } private val ReaderPage.cacheKey - get() = "${chapter.chapter.mangaId}-${chapter.chapter.index}-$index" + get() = "${chapter.chapter.id}-$url" private fun DiskCache.Snapshot.source(): BufferedSource = FileSystem.SYSTEM.source(data).buffer() diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/ReaderPage.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/ReaderPage.kt index 107fa287..4403239d 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/ReaderPage.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/model/ReaderPage.kt @@ -14,7 +14,8 @@ import kotlinx.coroutines.flow.MutableStateFlow @Immutable data class ReaderPage( - val index: Int, + val url: String, + val index2: Int, val bitmap: MutableStateFlow ImageDecodeState)?>>, val bitmapInfo: MutableStateFlow, val progress: MutableStateFlow, diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt index 395bd2df..9e01432e 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Continuous.kt @@ -126,7 +126,7 @@ fun ContinuousReader( } fun retry(index: Int) { - pages.find { it is ReaderPage && it.index == index }?.let { retry(it as ReaderPage) } + pages.find { it is ReaderPage && it.index2 == index }?.let { retry(it as ReaderPage) } } when (direction.isVertical) { @@ -199,7 +199,7 @@ private fun LazyListScope.items( pages, key = { when (it) { - is ReaderPage -> it.chapter.chapter.index to it.index + is ReaderPage -> it.chapter.chapter.index to it.index2 is ReaderPageSeparator -> it.previousChapter?.chapter?.index to it.nextChapter?.chapter?.index } }, @@ -207,7 +207,7 @@ private fun LazyListScope.items( when (image) { is ReaderPage -> Box(modifier, contentAlignment = Alignment.Center) { ReaderImage( - imageIndex = image.index, + imageIndex = image.index2, drawableHolder = image.bitmap.collectAsState().value, bitmapInfo = image.bitmapInfo.collectAsState().value, progress = image.progress.collectAsState().value, diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt index f717647d..b0b8e10d 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/viewer/Pager.kt @@ -84,7 +84,7 @@ fun PagerReader( val modifier = parentModifier then Modifier.fillMaxSize() fun retry(index: Int) { - pages.find { it is ReaderPage && it.index == index }?.let { retry(it as ReaderPage) } + pages.find { it is ReaderPage && it.index2 == index }?.let { retry(it as ReaderPage) } } if (direction.isVertical) { @@ -95,7 +95,7 @@ fun PagerReader( modifier = modifier, key = { when (val page = pages.getOrNull(it)) { - is ReaderPage -> page.chapter.chapter.index to page.index + is ReaderPage -> page.chapter.chapter.index to page.index2 is ReaderPageSeparator -> page.previousChapter?.chapter?.index to page.nextChapter?.chapter?.index else -> it } @@ -118,7 +118,7 @@ fun PagerReader( modifier = modifier, key = { when (val page = pages.getOrNull(it)) { - is ReaderPage -> page.chapter.chapter.index to page.index + is ReaderPage -> page.chapter.chapter.index to page.index2 is ReaderPageSeparator -> page.previousChapter?.chapter?.index to page.nextChapter?.chapter?.index else -> it } @@ -148,7 +148,7 @@ fun HandlePager( when (val image = pages[page]) { is ReaderPage -> { ReaderImage( - imageIndex = image.index, + imageIndex = image.index2, drawableHolder = image.bitmap.collectAsState().value, bitmapInfo = image.bitmapInfo.collectAsState().value, progress = image.progress.collectAsState().value, diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/UpdatesScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/UpdatesScreenViewModel.kt index bd1a0ab1..5d82e035 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/UpdatesScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/UpdatesScreenViewModel.kt @@ -7,8 +7,8 @@ package ca.gosyer.jui.ui.updates import ca.gosyer.jui.core.lang.launchDefault -import ca.gosyer.jui.domain.chapter.interactor.BatchUpdateChapter import ca.gosyer.jui.domain.chapter.interactor.DeleteChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.UpdateChapter import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.download.interactor.BatchChapterDownload import ca.gosyer.jui.domain.download.interactor.QueueChapterDownload @@ -47,7 +47,7 @@ class UpdatesScreenViewModel private val stopChapterDownload: StopChapterDownload, private val deleteChapterDownload: DeleteChapterDownload, private val getRecentUpdates: GetRecentUpdates, - private val batchUpdateChapter: BatchUpdateChapter, + private val updateChapter: UpdateChapter, private val batchChapterDownload: BatchChapterDownload, private val updateLibrary: UpdateLibrary, private val updatesPager: UpdatesPager, @@ -119,7 +119,7 @@ class UpdatesScreenViewModel read: Boolean, ) { scope.launch { - batchUpdateChapter.await(chapterIds, isRead = read, onError = { toast(it.message.orEmpty()) }) + updateChapter.await(chapterIds, read = read, onError = { toast(it.message.orEmpty()) }) _selectedIds.value = persistentListOf() } } @@ -133,7 +133,7 @@ class UpdatesScreenViewModel bookmark: Boolean, ) { scope.launch { - batchUpdateChapter.await(chapterIds, isBookmarked = bookmark, onError = { toast(it.message.orEmpty()) }) + updateChapter.await(chapterIds, bookmarked = bookmark, onError = { toast(it.message.orEmpty()) }) _selectedIds.value = persistentListOf() } } @@ -158,7 +158,7 @@ class UpdatesScreenViewModel scope.launchDefault { if (chapter == null) { val selectedIds = _selectedIds.value - batchUpdateChapter.await(selectedIds, delete = true, onError = { toast(it.message.orEmpty()) }) + deleteChapterDownload.await(selectedIds, onError = { toast(it.message.orEmpty()) }) selectedItems.value.forEach { it.setNotDownloaded() } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/components/UpdatesScreenContent.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/components/UpdatesScreenContent.kt index dc72974a..a886b07a 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/components/UpdatesScreenContent.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/updates/components/UpdatesScreenContent.kt @@ -77,7 +77,7 @@ fun UpdatesScreenContent( inActionMode: Boolean, selectedItems: ImmutableList, loadNextPage: () -> Unit, - openChapter: (index: Int, mangaId: Long) -> Unit, + openChapter: (id: Long, mangaId: Long) -> Unit, openManga: (Long) -> Unit, markRead: (Long?) -> Unit, markUnread: (Long?) -> Unit, @@ -201,7 +201,7 @@ fun UpdatesScreenContent( } } } else { - { openChapter(chapter.index, manga.id) } + { openChapter(chapter.id, manga.id) } }, markRead = markRead, markUnread = markUnread, diff --git a/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/reader/DesktopReaderMenu.kt b/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/reader/DesktopReaderMenu.kt index 2448856b..7e35eb2a 100644 --- a/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/reader/DesktopReaderMenu.kt +++ b/presentation/src/desktopMain/kotlin/ca/gosyer/jui/ui/reader/DesktopReaderMenu.kt @@ -24,13 +24,13 @@ import ca.gosyer.jui.ui.util.lang.launchApplication import kotlinx.coroutines.DelicateCoroutinesApi actual class ReaderLauncher { - private var isOpen by mutableStateOf?>(null) + private var isOpen by mutableStateOf?>(null) actual fun launch( - chapterIndex: Int, + chapterId: Long, mangaId: Long, ) { - isOpen = chapterIndex to mangaId + isOpen = chapterId to mangaId } @OptIn(DelicateCoroutinesApi::class) @@ -38,7 +38,7 @@ actual class ReaderLauncher { actual fun Reader() { val localParams = currentCompositionLocalContext DisposableEffect(isOpen) { - isOpen?.let { (chapterIndex, mangaId) -> + isOpen?.let { (chapterId, mangaId) -> launchApplication { val windowState = rememberWindowState( position = WindowPosition.Aligned(Alignment.Center), @@ -52,7 +52,7 @@ actual class ReaderLauncher { state = windowState, ) { ReaderMenu( - chapterIndex = chapterIndex, + chapterId = chapterId, mangaId = mangaId, onCloseRequest = ::exitApplication, ) diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt index bec36deb..0d3f10e8 100644 --- a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/reader/IosReaderMenu.kt @@ -14,14 +14,14 @@ import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow class ReaderScreen( - val chapterIndex: Int, + val chapterId: Long, val mangaId: Long, ) : Screen { @Composable override fun Content() { val navigator = LocalNavigator.currentOrThrow ReaderMenu( - chapterIndex, + chapterId, mangaId, navigator::pop, ) @@ -32,10 +32,10 @@ actual class ReaderLauncher( private val navigator: Navigator?, ) { actual fun launch( - chapterIndex: Int, + chapterId: Long, mangaId: Long, ) { - navigator?.push(ReaderScreen(chapterIndex, mangaId)) + navigator?.push(ReaderScreen(chapterId, mangaId)) } @Composable