From b88c16c13b94351596cd4b71260f2d8204a930c4 Mon Sep 17 00:00:00 2001 From: Syer10 Date: Fri, 1 Jul 2022 14:39:44 -0400 Subject: [PATCH] Use interactors for chapter data calls --- .../interactor/DeleteChapterDownload.kt | 40 ++++++ .../domain/chapter/interactor/GetChapter.kt | 40 ++++++ .../chapter/interactor/GetChapterPage.kt | 69 ++++++++++ .../domain/chapter/interactor/GetChapters.kt | 34 +++++ .../interactor/QueueChapterDownload.kt | 40 ++++++ .../chapter/interactor/RefreshChapters.kt | 33 +++++ .../chapter/interactor/StopChapterDownload.kt | 41 ++++++ .../chapter/interactor/UpdateChapterFlags.kt | 101 ++++++++++++++ .../ui/base/chapter/ChapterDownloadButtons.kt | 22 ++- .../ui/downloads/DownloadsScreenViewModel.kt | 32 ++--- .../jui/ui/manga/MangaScreenViewModel.kt | 126 +++++++----------- .../ca/gosyer/jui/ui/reader/ChapterLoader.kt | 6 +- .../jui/ui/reader/ReaderMenuViewModel.kt | 33 +++-- .../ui/reader/loader/TachideskPageLoader.kt | 9 +- .../jui/ui/updates/UpdatesScreenViewModel.kt | 54 ++++---- 15 files changed, 513 insertions(+), 167 deletions(-) create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/DeleteChapterDownload.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapter.kt create 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/GetChapters.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/QueueChapterDownload.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/RefreshChapters.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/StopChapterDownload.kt create mode 100644 domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterFlags.kt 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 new file mode 100644 index 00000000..0139f41d --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/DeleteChapterDownload.kt @@ -0,0 +1,40 @@ +/* + * 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.ChapterRepository +import ca.gosyer.jui.domain.manga.model.Manga +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect +import me.tatarka.inject.annotations.Inject +import org.lighthousegames.logging.logging + +class DeleteChapterDownload @Inject constructor(private val chapterRepository: ChapterRepository) { + + suspend fun await(mangaId: Long, index: Int) = asFlow(mangaId, index) + .catch { log.warn(it) { "Failed to delete chapter download for $index of $mangaId" } } + .collect() + + suspend fun await(manga: Manga, index: Int) = asFlow(manga, index) + .catch { log.warn(it) { "Failed to delete chapter download for $index of ${manga.title}(${manga.id})" } } + .collect() + + suspend fun await(chapter: Chapter) = asFlow(chapter) + .catch { log.warn(it) { "Failed to delete chapter download for ${chapter.index} of ${chapter.mangaId}" } } + .collect() + + fun asFlow(mangaId: Long, index: Int) = chapterRepository.deleteChapterDownload(mangaId, index) + + fun asFlow(manga: Manga, index: Int) = chapterRepository.deleteChapterDownload(manga.id, index) + + fun asFlow(chapter: Chapter) = chapterRepository.deleteChapterDownload(chapter.mangaId, chapter.index) + + 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 new file mode 100644 index 00000000..65a34f91 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapter.kt @@ -0,0 +1,40 @@ +/* + * 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.ChapterRepository +import ca.gosyer.jui.domain.manga.model.Manga +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.singleOrNull +import me.tatarka.inject.annotations.Inject +import org.lighthousegames.logging.logging + +class GetChapter @Inject constructor(private val chapterRepository: ChapterRepository) { + + suspend fun await(mangaId: Long, index: Int) = asFlow(mangaId, index) + .catch { log.warn(it) { "Failed to get chapter $index for $mangaId" } } + .singleOrNull() + + suspend fun await(manga: Manga, index: Int) = asFlow(manga, index) + .catch { log.warn(it) { "Failed to get chapter $index for ${manga.title}(${manga.id})" } } + .singleOrNull() + + suspend fun await(chapter: Chapter) = asFlow(chapter) + .catch { log.warn(it) { "Failed to get chapter ${chapter.index} for ${chapter.mangaId}" } } + .singleOrNull() + + fun asFlow(mangaId: Long, index: Int) = chapterRepository.getChapter(mangaId, index) + + fun asFlow(manga: Manga, index: Int) = chapterRepository.getChapter(manga.id, index) + + fun asFlow(chapter: Chapter) = chapterRepository.getChapter(chapter.mangaId, chapter.index) + + companion object { + private val log = logging() + } +} 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 new file mode 100644 index 00000000..2a5b9db5 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapterPage.kt @@ -0,0 +1,69 @@ +/* + * 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.ChapterRepository +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 chapterRepository: ChapterRepository) { + + suspend fun await( + mangaId: Long, + index: Int, + pageNum: Int, + block: HttpRequestBuilder.() -> Unit + ) = asFlow(mangaId, index, pageNum, block) + .catch { 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 + ) = asFlow(manga, index, pageNum, block) + .catch { 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 + ) = asFlow(chapter, pageNum, block) + .catch { 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 + ) = chapterRepository.getPage(mangaId, index, pageNum, block) + + fun asFlow( + manga: Manga, + index: Int, + pageNum: Int, + block: HttpRequestBuilder.() -> Unit + ) = chapterRepository.getPage(manga.id, index, pageNum, block) + + fun asFlow( + chapter: Chapter, + pageNum: Int, + block: HttpRequestBuilder.() -> Unit + ) = chapterRepository.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/GetChapters.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapters.kt new file mode 100644 index 00000000..89b85d4b --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/GetChapters.kt @@ -0,0 +1,34 @@ +/* + * 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 ca.gosyer.jui.domain.manga.model.Manga +import ca.gosyer.jui.domain.manga.service.MangaRepository +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.singleOrNull +import me.tatarka.inject.annotations.Inject +import org.lighthousegames.logging.logging + +class GetChapters @Inject constructor(private val chapterRepository: ChapterRepository) { + + suspend fun await(mangaId: Long) = asFlow(mangaId) + .catch { log.warn(it) { "Failed to get chapters for $mangaId" } } + .singleOrNull() + + suspend fun await(manga: Manga) = asFlow(manga) + .catch { log.warn(it) { "Failed to get chapters for ${manga.title}(${manga.id})" } } + .singleOrNull() + + fun asFlow(mangaId: Long) = chapterRepository.getChapters(mangaId) + + fun asFlow(manga: Manga) = chapterRepository.getChapters(manga.id) + + companion object { + private val log = logging() + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/QueueChapterDownload.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/QueueChapterDownload.kt new file mode 100644 index 00000000..20cb17cf --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/QueueChapterDownload.kt @@ -0,0 +1,40 @@ +/* + * 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.ChapterRepository +import ca.gosyer.jui.domain.manga.model.Manga +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect +import me.tatarka.inject.annotations.Inject +import org.lighthousegames.logging.logging + +class QueueChapterDownload @Inject constructor(private val chapterRepository: ChapterRepository) { + + suspend fun await(mangaId: Long, index: Int) = asFlow(mangaId, index) + .catch { log.warn(it) { "Failed to queue chapter $index of $mangaId for a download" } } + .collect() + + suspend fun await(manga: Manga, index: Int) = asFlow(manga, index) + .catch { log.warn(it) { "Failed to queue chapter $index of ${manga.title}(${manga.id}) for a download" } } + .collect() + + suspend fun await(chapter: Chapter) = asFlow(chapter) + .catch { log.warn(it) { "Failed to queue chapter ${chapter.index} of ${chapter.mangaId} for a download" } } + .collect() + + fun asFlow(mangaId: Long, index: Int) = chapterRepository.queueChapterDownload(mangaId, index) + + fun asFlow(manga: Manga, index: Int) = chapterRepository.queueChapterDownload(manga.id, index) + + fun asFlow(chapter: Chapter) = chapterRepository.queueChapterDownload(chapter.mangaId, chapter.index) + + companion object { + private val log = logging() + } +} 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 new file mode 100644 index 00000000..8b356281 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/RefreshChapters.kt @@ -0,0 +1,33 @@ +/* + * 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 ca.gosyer.jui.domain.manga.model.Manga +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.singleOrNull +import me.tatarka.inject.annotations.Inject +import org.lighthousegames.logging.logging + +class RefreshChapters @Inject constructor(private val chapterRepository: ChapterRepository) { + + suspend fun await(mangaId: Long) = asFlow(mangaId) + .catch { log.warn(it) { "Failed to refresh chapters for $mangaId" } } + .singleOrNull() + + suspend fun await(manga: Manga) = asFlow(manga) + .catch { log.warn(it) { "Failed to refresh chapters for ${manga.title}(${manga.id})" } } + .singleOrNull() + + fun asFlow(mangaId: Long) = chapterRepository.getChapters(mangaId, true) + + fun asFlow(manga: Manga) = chapterRepository.getChapters(manga.id, true) + + companion object { + private val log = logging() + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/StopChapterDownload.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/StopChapterDownload.kt new file mode 100644 index 00000000..346d5772 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/StopChapterDownload.kt @@ -0,0 +1,41 @@ +/* + * 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.ChapterRepository +import ca.gosyer.jui.domain.manga.model.Manga +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.singleOrNull +import me.tatarka.inject.annotations.Inject +import org.lighthousegames.logging.logging + +class StopChapterDownload @Inject constructor(private val chapterRepository: ChapterRepository) { + + suspend fun await(mangaId: Long, index: Int) = asFlow(mangaId, index) + .catch { log.warn(it) { "Failed to stop chapter download for $index of $mangaId" } } + .collect() + + suspend fun await(manga: Manga, index: Int) = asFlow(manga, index) + .catch { log.warn(it) { "Failed to stop chapter download for $index of ${manga.title}(${manga.id})" } } + .collect() + + suspend fun await(chapter: Chapter) = asFlow(chapter) + .catch { log.warn(it) { "Failed to stop chapter download for ${chapter.index} of ${chapter.mangaId}" } } + .collect() + + fun asFlow(mangaId: Long, index: Int) = chapterRepository.stopChapterDownload(mangaId, index) + + fun asFlow(manga: Manga, index: Int) = chapterRepository.stopChapterDownload(manga.id, index) + + fun asFlow(chapter: Chapter) = chapterRepository.stopChapterDownload(chapter.mangaId, chapter.index) + + companion object { + private val log = logging() + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterFlags.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterFlags.kt new file mode 100644 index 00000000..809bf954 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterFlags.kt @@ -0,0 +1,101 @@ +/* + * 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.ChapterRepository +import ca.gosyer.jui.domain.manga.model.Manga +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect +import me.tatarka.inject.annotations.Inject +import org.lighthousegames.logging.logging + +class UpdateChapterFlags @Inject constructor(private val chapterRepository: ChapterRepository) { + + suspend fun await( + mangaId: Long, + index: Int, + read: Boolean? = null, + bookmarked: Boolean? = null, + lastPageRead: Int? = null, + markPreviousRead: Boolean? = null + ) = asFlow(mangaId, index, read, bookmarked, lastPageRead, markPreviousRead) + .catch { log.warn(it) { "Failed to update chapter flags for chapter $index of $mangaId" } } + .collect() + + suspend fun await( + manga: Manga, + index: Int, + read: Boolean? = null, + bookmarked: Boolean? = null, + lastPageRead: Int? = null, + markPreviousRead: Boolean? = null + ) = asFlow(manga, index, read, bookmarked, lastPageRead, markPreviousRead) + .catch { log.warn(it) { "Failed to update chapter flags for chapter $index of ${manga.title}(${manga.id})" } } + .collect() + + suspend fun await( + chapter: Chapter, + read: Boolean? = null, + bookmarked: Boolean? = null, + lastPageRead: Int? = null, + markPreviousRead: Boolean? = null + ) = asFlow(chapter, read, bookmarked, lastPageRead, markPreviousRead) + .catch { log.warn(it) { "Failed to update chapter flags for chapter ${chapter.index} of ${chapter.mangaId}" } } + .collect() + + fun asFlow( + mangaId: Long, + index: Int, + read: Boolean? = null, + bookmarked: Boolean? = null, + lastPageRead: Int? = null, + markPreviousRead: Boolean? = null + ) = chapterRepository.updateChapter( + mangaId = mangaId, + chapterIndex = index, + read = read, + bookmarked = bookmarked, + lastPageRead = lastPageRead, + markPreviousRead = markPreviousRead + ) + + fun asFlow( + manga: Manga, + index: Int, + read: Boolean? = null, + bookmarked: Boolean? = null, + lastPageRead: Int? = null, + markPreviousRead: Boolean? = null + ) = chapterRepository.updateChapter( + mangaId = manga.id, + chapterIndex = index, + read = read, + bookmarked = bookmarked, + lastPageRead = lastPageRead, + markPreviousRead = markPreviousRead + ) + + fun asFlow( + chapter: Chapter, + read: Boolean? = null, + bookmarked: Boolean? = null, + lastPageRead: Int? = null, + markPreviousRead: Boolean? = null + ) = chapterRepository.updateChapter( + mangaId = chapter.mangaId, + chapterIndex = chapter.index, + read = read, + bookmarked = bookmarked, + lastPageRead = lastPageRead, + markPreviousRead = markPreviousRead + ) + + companion object { + private val log = logging() + } +} diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/chapter/ChapterDownloadButtons.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/chapter/ChapterDownloadButtons.kt index 72279e0c..fc721fc2 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/chapter/ChapterDownloadButtons.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/chapter/ChapterDownloadButtons.kt @@ -31,7 +31,8 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl +import ca.gosyer.jui.domain.chapter.interactor.DeleteChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.StopChapterDownload import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.download.model.DownloadChapter import ca.gosyer.jui.domain.download.model.DownloadState @@ -40,11 +41,8 @@ import ca.gosyer.jui.i18n.MR import ca.gosyer.jui.uicore.components.DropdownIconButton import ca.gosyer.jui.uicore.components.DropdownMenuItem import ca.gosyer.jui.uicore.resources.stringResource -import io.ktor.client.statement.HttpResponse -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.onEach data class ChapterDownloadItem( val manga: Manga?, @@ -74,18 +72,14 @@ data class ChapterDownloadItem( _downloadChapterFlow.value = downloadingChapter } - fun deleteDownload(chapterHandler: ChapterRepositoryImpl): Flow { - return chapterHandler.deleteChapterDownload(chapter.mangaId, chapter.index) - .onEach { - _downloadState.value = ChapterDownloadState.NotDownloaded - } + suspend fun deleteDownload(deleteChapterDownload: DeleteChapterDownload) { + deleteChapterDownload.await(chapter) + _downloadState.value = ChapterDownloadState.NotDownloaded } - fun stopDownloading(chapterHandler: ChapterRepositoryImpl): Flow { - return chapterHandler.stopChapterDownload(chapter.mangaId, chapter.index) - .onEach { - _downloadState.value = ChapterDownloadState.NotDownloaded - } + suspend fun stopDownloading(stopChapterDownload: StopChapterDownload) { + stopChapterDownload.await(chapter) + _downloadState.value = ChapterDownloadState.NotDownloaded } } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/downloads/DownloadsScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/downloads/DownloadsScreenViewModel.kt index a48547d0..a4ea355a 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/downloads/DownloadsScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/downloads/DownloadsScreenViewModel.kt @@ -6,8 +6,9 @@ package ca.gosyer.jui.ui.downloads -import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl import ca.gosyer.jui.domain.base.WebsocketService.Actions +import ca.gosyer.jui.domain.chapter.interactor.QueueChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.StopChapterDownload import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.download.interactor.ClearDownloadQueue import ca.gosyer.jui.domain.download.interactor.StartDownloading @@ -19,10 +20,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import me.tatarka.inject.annotations.Inject import org.lighthousegames.logging.logging @@ -32,7 +29,8 @@ class DownloadsScreenViewModel @Inject constructor( private val startDownloading: StartDownloading, private val stopDownloading: StopDownloading, private val clearDownloadQueue: ClearDownloadQueue, - private val chapterHandler: ChapterRepositoryImpl, + private val queueChapterDownload: QueueChapterDownload, + private val stopChapterDownload: StopChapterDownload, private val contextWrapper: ContextWrapper, standalone: Boolean ) : ViewModel(contextWrapper) { @@ -60,26 +58,14 @@ class DownloadsScreenViewModel @Inject constructor( } fun stopDownload(chapter: Chapter) { - chapterHandler.stopChapterDownload(chapter.mangaId, chapter.index) - .catch { - log.warn(it) { "Error stop chapter download" } - } - .launchIn(scope) + scope.launch { stopChapterDownload.await(chapter) } } fun moveToBottom(chapter: Chapter) { - chapterHandler.stopChapterDownload(chapter.mangaId, chapter.index) - .onEach { - chapterHandler.queueChapterDownload(chapter.mangaId, chapter.index) - .catch { - log.warn(it) { "Error adding download" } - } - .collect() - } - .catch { - log.warn(it) { "Error stop chapter download" } - } - .launchIn(scope) + scope.launch { + stopChapterDownload.await(chapter) + queueChapterDownload.await(chapter) + } } fun restartDownloader() = startDownloadService(contextWrapper, downloadService, Actions.RESTART) 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 d4a1ebc5..c49d1fe9 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 @@ -8,12 +8,17 @@ package ca.gosyer.jui.ui.manga import ca.gosyer.jui.core.lang.withIOContext import ca.gosyer.jui.data.base.DateHandler -import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl import ca.gosyer.jui.domain.category.interactor.AddMangaToCategory 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.DeleteChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.GetChapters +import ca.gosyer.jui.domain.chapter.interactor.QueueChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.RefreshChapters +import ca.gosyer.jui.domain.chapter.interactor.StopChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterFlags import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.download.service.DownloadService import ca.gosyer.jui.domain.library.interactor.AddMangaToLibrary @@ -31,11 +36,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.single import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import me.tatarka.inject.annotations.Inject @@ -45,7 +48,12 @@ class MangaScreenViewModel @Inject constructor( private val dateHandler: DateHandler, private val getManga: GetManga, private val refreshManga: RefreshManga, - private val chapterHandler: ChapterRepositoryImpl, + private val getChapters: GetChapters, + private val refreshChapters: RefreshChapters, + private val updateChapterFlags: UpdateChapterFlags, + private val queueChapterDownload: QueueChapterDownload, + private val stopChapterDownload: StopChapterDownload, + private val deleteChapterDownload: DeleteChapterDownload, private val getCategories: GetCategories, private val getMangaCategories: GetMangaCategories, private val addMangaToCategory: AddMangaToCategory, @@ -138,23 +146,31 @@ class MangaScreenViewModel @Inject constructor( } if (manga != null) { _manga.value = manga + } else { + // TODO: 2022-07-01 Error toast + } + + val mangaCategories = getMangaCategories.await(mangaId) + if (mangaCategories != null) { + _mangaCategories.value = mangaCategories + } else { + // TODO: 2022-07-01 Error toast } - getMangaCategories.await(mangaId) - ?.let { - _mangaCategories.value = it - } } } private suspend fun refreshChaptersAsync(mangaId: Long, refresh: Boolean = false) = withIOContext { async { - _chapters.value = chapterHandler.getChapters(mangaId, refresh) - .catch { - log.warn(it) { "Error getting chapters" } - emit(emptyList()) - } - .single() - .toDownloadChapters() + val chapters = if (refresh) { + refreshChapters.await(mangaId) + } else { + getChapters.await(mangaId) + } + if (chapters != null) { + _chapters.value = chapters.toDownloadChapters() + } else { + // TODO: 2022-07-01 Error toast + } } } @@ -193,48 +209,24 @@ class MangaScreenViewModel @Inject constructor( } } + private fun findChapter(index: Int) = chapters.value.find { it.chapter.index == index }?.chapter + fun toggleRead(index: Int) { + val chapter = findChapter(index) ?: return scope.launch { manga.value?.let { manga -> - chapterHandler.updateChapter( - manga.id, - index, - read = !_chapters.value.first { it.chapter.index == index }.chapter.read - ) - .catch { - log.warn(it) { "Error toggling read" } - } - .collect() - _chapters.value = chapterHandler.getChapters(manga.id) - .catch { - log.warn(it) { "Error getting new chapters after toggling read" } - emit(emptyList()) - } - .single() - .toDownloadChapters() + updateChapterFlags.await(manga, index, read = chapter.read.not()) + refreshChaptersAsync(manga.id).await() } } } fun toggleBookmarked(index: Int) { + val chapter = findChapter(index) ?: return scope.launch { manga.value?.let { manga -> - chapterHandler.updateChapter( - manga.id, - index, - bookmarked = !_chapters.value.first { it.chapter.index == index }.chapter.bookmarked - ) - .catch { - log.warn(it) { "Error toggling bookmarked" } - } - .collect() - _chapters.value = chapterHandler.getChapters(manga.id) - .catch { - log.warn(it) { "Error getting new chapters after toggling bookmarked" } - emit(emptyList()) - } - .single() - .toDownloadChapters() + updateChapterFlags.await(manga, index, bookmarked = chapter.bookmarked.not()) + refreshChaptersAsync(manga.id).await() } } } @@ -242,48 +234,30 @@ class MangaScreenViewModel @Inject constructor( fun markPreviousRead(index: Int) { scope.launch { manga.value?.let { manga -> - chapterHandler.updateChapter(manga.id, index, markPreviousRead = true) - .catch { - log.warn(it) { "Error marking previous as read" } - } - .collect() - _chapters.value = chapterHandler.getChapters(manga.id) - .catch { - log.warn(it) { "Error getting new chapters after marking previous as read" } - emit(emptyList()) - } - .single() - .toDownloadChapters() + updateChapterFlags.await(manga, index, markPreviousRead = true) + refreshChaptersAsync(manga.id).await() } } } fun downloadChapter(index: Int) { manga.value?.let { manga -> - chapterHandler.queueChapterDownload(manga.id, index) - .catch { - log.warn(it) { "Error downloading chapter" } - } - .launchIn(scope) + scope.launch { queueChapterDownload.await(manga, index) } } } fun deleteDownload(index: Int) { - chapters.value.find { it.chapter.index == index } - ?.deleteDownload(chapterHandler) - ?.catch { - log.warn(it) { "Error deleting download" } - } - ?.launchIn(scope) + scope.launch { + chapters.value.find { it.chapter.index == index } + ?.deleteDownload(deleteChapterDownload) + } } fun stopDownloadingChapter(index: Int) { - chapters.value.find { it.chapter.index == index } - ?.stopDownloading(chapterHandler) - ?.catch { - log.warn(it) { "Error stopping download" } - } - ?.launchIn(scope) + scope.launch { + chapters.value.find { it.chapter.index == index } + ?.stopDownloading(stopChapterDownload) + } } private fun List.toDownloadChapters() = map { 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 35003a01..400c16ec 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,7 +6,7 @@ package ca.gosyer.jui.ui.reader -import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl +import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage import ca.gosyer.jui.domain.reader.service.ReaderPreferences import ca.gosyer.jui.ui.reader.loader.TachideskPageLoader import ca.gosyer.jui.ui.reader.model.ReaderChapter @@ -20,7 +20,7 @@ import org.lighthousegames.logging.logging class ChapterLoader( private val readerPreferences: ReaderPreferences, - private val chapterHandler: ChapterRepositoryImpl + private val getChapterPage: GetChapterPage ) { fun loadChapter(chapter: ReaderChapter): StateFlow> { if (chapterIsReady(chapter)) { @@ -29,7 +29,7 @@ class ChapterLoader( chapter.state = ReaderChapter.State.Loading log.debug { "Loading pages for ${chapter.chapter.name}" } - val loader = TachideskPageLoader(chapter, readerPreferences, chapterHandler) + val loader = TachideskPageLoader(chapter, readerPreferences, getChapterPage) val pages = loader.getPages() 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 61ce707a..2ddf30ef 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 @@ -8,7 +8,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.data.chapter.ChapterRepositoryImpl +import ca.gosyer.jui.domain.chapter.interactor.GetChapter +import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage +import ca.gosyer.jui.domain.chapter.interactor.GetChapters +import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterFlags import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMeta import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.manga.interactor.GetManga @@ -51,7 +54,10 @@ import org.lighthousegames.logging.logging class ReaderMenuViewModel @Inject constructor( private val readerPreferences: ReaderPreferences, private val getManga: GetManga, - private val chapterHandler: ChapterRepositoryImpl, + private val getChapters: GetChapters, + private val getChapter: GetChapter, + private val getChapterPage: GetChapterPage, + private val updateChapterFlags: UpdateChapterFlags, private val updateMangaMeta: UpdateMangaMeta, private val updateChapterMeta: UpdateChapterMeta, contextWrapper: ContextWrapper, @@ -101,7 +107,7 @@ class ReaderMenuViewModel @Inject constructor( val readerModeSettings = ReaderModeWatch(readerPreferences, scope, readerMode) - private val loader = ChapterLoader(readerPreferences, chapterHandler) + private val loader = ChapterLoader(readerPreferences, getChapterPage) init { init() @@ -217,7 +223,7 @@ class ReaderMenuViewModel @Inject constructor( private suspend fun initChapters(mangaId: Long, chapterIndex: Int) { resetValues() val chapter = ReaderChapter( - chapterHandler.getChapter(mangaId, chapterIndex) + getChapter.asFlow(mangaId, chapterIndex) .catch { _state.value = ReaderChapter.State.Error(it) log.warn(it) { "Error getting chapter" } @@ -227,9 +233,10 @@ class ReaderMenuViewModel @Inject constructor( val pages = loader.loadChapter(chapter) viewerChapters.currChapter.value = chapter scope.launchDefault { - val chapters = chapterHandler.getChapters(mangaId) + val chapters = getChapters.asFlow(mangaId) .catch { log.warn(it) { "Error getting chapter list" } + // TODO: 2022-07-01 Error toast emit(emptyList()) } .single() @@ -273,29 +280,21 @@ class ReaderMenuViewModel @Inject constructor( .onEach { index -> pages.value.getOrNull(_currentPage.value - 1)?.let { chapter.pageLoader?.loadPage(it) } if (index == pages.value.size) { - markChapterRead(mangaId, chapter) + markChapterRead(chapter) } } .launchIn(chapter.scope) } - private fun markChapterRead(mangaId: Long, chapter: ReaderChapter) { - chapterHandler.updateChapter(mangaId, chapter.chapter.index, true) - .catch { - log.warn(it) { "Error marking chapter read" } - } - .launchIn(scope) + private fun markChapterRead(chapter: ReaderChapter) { + scope.launch { updateChapterFlags.await(chapter.chapter, read = true) } } @OptIn(DelicateCoroutinesApi::class) fun sendProgress(chapter: Chapter? = this.chapter.value?.chapter, lastPageRead: Int = currentPage.value) { chapter ?: return if (chapter.read) return - chapterHandler.updateChapter(chapter.mangaId, chapter.index, lastPageRead = lastPageRead) - .catch { - log.warn(it) { "Error sending progress" } - } - .launchIn(GlobalScope) + GlobalScope.launch { updateChapterFlags.await(chapter, lastPageRead = lastPageRead) } } fun updateLastPageReadOffset(offset: Int) { 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 ea6bb52b..df3626e4 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,7 +8,7 @@ package ca.gosyer.jui.ui.reader.loader import ca.gosyer.jui.core.lang.IO import ca.gosyer.jui.core.lang.throwIfCancellation -import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl +import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage import ca.gosyer.jui.domain.reader.service.ReaderPreferences import ca.gosyer.jui.ui.reader.model.ReaderChapter import ca.gosyer.jui.ui.reader.model.ReaderPage @@ -33,7 +33,7 @@ import org.lighthousegames.logging.logging class TachideskPageLoader( val chapter: ReaderChapter, readerPreferences: ReaderPreferences, - chapterHandler: ChapterRepositoryImpl + getChapterPage: GetChapterPage ) : PageLoader() { val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) @@ -63,8 +63,7 @@ class TachideskPageLoader( val page = priorityPage.page log.debug { "Loading page ${page.index}" } if (page.status.value == ReaderPage.Status.QUEUE) { - chapterHandler - .getPage(chapter.chapter.mangaId, chapter.chapter.index, page.index) { + getChapterPage.asFlow(chapter.chapter, page.index) { onDownload { bytesSentTotal, contentLength -> page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F) } @@ -78,7 +77,7 @@ class TachideskPageLoader( page.bitmap.value = null page.status.value = ReaderPage.Status.ERROR page.error.value = it.message - log.warn(it) { "Error getting image" } + log.warn(it) { "Failed to get page ${page.index} for chapter ${chapter.chapter.index} for ${chapter.chapter.mangaId}" } } .flowOn(Dispatchers.IO) .collect() 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 1764cde0..1ee519e5 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 @@ -10,7 +10,9 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshots.SnapshotStateList -import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl +import ca.gosyer.jui.domain.chapter.interactor.DeleteChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.QueueChapterDownload +import ca.gosyer.jui.domain.chapter.interactor.StopChapterDownload import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.download.service.DownloadService import ca.gosyer.jui.domain.updates.interactor.GetRecentUpdates @@ -37,7 +39,9 @@ import me.tatarka.inject.annotations.Inject import org.lighthousegames.logging.logging class UpdatesScreenViewModel @Inject constructor( - private val chapterHandler: ChapterRepositoryImpl, + private val queueChapterDownload: QueueChapterDownload, + private val stopChapterDownload: StopChapterDownload, + private val deleteChapterDownload: DeleteChapterDownload, private val getRecentUpdates: GetRecentUpdates, contextWrapper: ContextWrapper ) : ViewModel(contextWrapper) { @@ -117,41 +121,33 @@ class UpdatesScreenViewModel @Inject constructor( } fun downloadChapter(chapter: Chapter) { - chapterHandler.queueChapterDownload(chapter.mangaId, chapter.index) - .catch { - log.warn(it) { "Error queueing chapter" } - } - .launchIn(scope) + scope.launch { queueChapterDownload.await(chapter) } } fun deleteDownloadedChapter(chapter: Chapter) { - _updates - .firstNotNullOfOrNull { (_, chapters) -> - chapters.find { - it.chapter.mangaId == chapter.mangaId && - it.chapter.index == chapter.index + scope.launch { + _updates + .firstNotNullOfOrNull { (_, chapters) -> + chapters.find { + it.chapter.mangaId == chapter.mangaId && + it.chapter.index == chapter.index + } } - } - ?.deleteDownload(chapterHandler) - ?.catch { - log.warn(it) { "Error deleting download" } - } - ?.launchIn(scope) + ?.deleteDownload(deleteChapterDownload) + } } fun stopDownloadingChapter(chapter: Chapter) { - _updates - .firstNotNullOfOrNull { (_, chapters) -> - chapters.find { - it.chapter.mangaId == chapter.mangaId && - it.chapter.index == chapter.index + scope.launch { + _updates + .firstNotNullOfOrNull { (_, chapters) -> + chapters.find { + it.chapter.mangaId == chapter.mangaId && + it.chapter.index == chapter.index + } } - } - ?.stopDownloading(chapterHandler) - ?.catch { - log.warn(it) { "Error stopping download" } - } - ?.launchIn(scope) + ?.stopDownloading(stopChapterDownload) + } } private companion object {