Use interactors for chapter data calls

This commit is contained in:
Syer10
2022-07-01 14:39:44 -04:00
parent 1bcf6f09c5
commit b88c16c13b
15 changed files with 513 additions and 167 deletions

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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<HttpResponse> {
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<HttpResponse> {
return chapterHandler.stopChapterDownload(chapter.mangaId, chapter.index)
.onEach {
_downloadState.value = ChapterDownloadState.NotDownloaded
}
suspend fun stopDownloading(stopChapterDownload: StopChapterDownload) {
stopChapterDownload.await(chapter)
_downloadState.value = ChapterDownloadState.NotDownloaded
}
}

View File

@@ -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)

View File

@@ -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<Chapter>.toDownloadChapters() = map {

View File

@@ -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<List<ReaderPage>> {
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()

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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 {