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.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp 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.chapter.model.Chapter
import ca.gosyer.jui.domain.download.model.DownloadChapter import ca.gosyer.jui.domain.download.model.DownloadChapter
import ca.gosyer.jui.domain.download.model.DownloadState 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.DropdownIconButton
import ca.gosyer.jui.uicore.components.DropdownMenuItem import ca.gosyer.jui.uicore.components.DropdownMenuItem
import ca.gosyer.jui.uicore.resources.stringResource 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.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.onEach
data class ChapterDownloadItem( data class ChapterDownloadItem(
val manga: Manga?, val manga: Manga?,
@@ -74,18 +72,14 @@ data class ChapterDownloadItem(
_downloadChapterFlow.value = downloadingChapter _downloadChapterFlow.value = downloadingChapter
} }
fun deleteDownload(chapterHandler: ChapterRepositoryImpl): Flow<HttpResponse> { suspend fun deleteDownload(deleteChapterDownload: DeleteChapterDownload) {
return chapterHandler.deleteChapterDownload(chapter.mangaId, chapter.index) deleteChapterDownload.await(chapter)
.onEach { _downloadState.value = ChapterDownloadState.NotDownloaded
_downloadState.value = ChapterDownloadState.NotDownloaded
}
} }
fun stopDownloading(chapterHandler: ChapterRepositoryImpl): Flow<HttpResponse> { suspend fun stopDownloading(stopChapterDownload: StopChapterDownload) {
return chapterHandler.stopChapterDownload(chapter.mangaId, chapter.index) stopChapterDownload.await(chapter)
.onEach { _downloadState.value = ChapterDownloadState.NotDownloaded
_downloadState.value = ChapterDownloadState.NotDownloaded
}
} }
} }

View File

@@ -6,8 +6,9 @@
package ca.gosyer.jui.ui.downloads 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.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.chapter.model.Chapter
import ca.gosyer.jui.domain.download.interactor.ClearDownloadQueue import ca.gosyer.jui.domain.download.interactor.ClearDownloadQueue
import ca.gosyer.jui.domain.download.interactor.StartDownloading import ca.gosyer.jui.domain.download.interactor.StartDownloading
@@ -19,10 +20,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.asStateFlow 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 kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
@@ -32,7 +29,8 @@ class DownloadsScreenViewModel @Inject constructor(
private val startDownloading: StartDownloading, private val startDownloading: StartDownloading,
private val stopDownloading: StopDownloading, private val stopDownloading: StopDownloading,
private val clearDownloadQueue: ClearDownloadQueue, private val clearDownloadQueue: ClearDownloadQueue,
private val chapterHandler: ChapterRepositoryImpl, private val queueChapterDownload: QueueChapterDownload,
private val stopChapterDownload: StopChapterDownload,
private val contextWrapper: ContextWrapper, private val contextWrapper: ContextWrapper,
standalone: Boolean standalone: Boolean
) : ViewModel(contextWrapper) { ) : ViewModel(contextWrapper) {
@@ -60,26 +58,14 @@ class DownloadsScreenViewModel @Inject constructor(
} }
fun stopDownload(chapter: Chapter) { fun stopDownload(chapter: Chapter) {
chapterHandler.stopChapterDownload(chapter.mangaId, chapter.index) scope.launch { stopChapterDownload.await(chapter) }
.catch {
log.warn(it) { "Error stop chapter download" }
}
.launchIn(scope)
} }
fun moveToBottom(chapter: Chapter) { fun moveToBottom(chapter: Chapter) {
chapterHandler.stopChapterDownload(chapter.mangaId, chapter.index) scope.launch {
.onEach { stopChapterDownload.await(chapter)
chapterHandler.queueChapterDownload(chapter.mangaId, chapter.index) queueChapterDownload.await(chapter)
.catch { }
log.warn(it) { "Error adding download" }
}
.collect()
}
.catch {
log.warn(it) { "Error stop chapter download" }
}
.launchIn(scope)
} }
fun restartDownloader() = startDownloadService(contextWrapper, downloadService, Actions.RESTART) 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.core.lang.withIOContext
import ca.gosyer.jui.data.base.DateHandler 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.AddMangaToCategory
import ca.gosyer.jui.domain.category.interactor.GetCategories import ca.gosyer.jui.domain.category.interactor.GetCategories
import ca.gosyer.jui.domain.category.interactor.GetMangaCategories import ca.gosyer.jui.domain.category.interactor.GetMangaCategories
import ca.gosyer.jui.domain.category.interactor.RemoveMangaFromCategory import ca.gosyer.jui.domain.category.interactor.RemoveMangaFromCategory
import ca.gosyer.jui.domain.category.model.Category 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.chapter.model.Chapter
import ca.gosyer.jui.domain.download.service.DownloadService import ca.gosyer.jui.domain.download.service.DownloadService
import ca.gosyer.jui.domain.library.interactor.AddMangaToLibrary 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.SharingStarted
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
@@ -45,7 +48,12 @@ class MangaScreenViewModel @Inject constructor(
private val dateHandler: DateHandler, private val dateHandler: DateHandler,
private val getManga: GetManga, private val getManga: GetManga,
private val refreshManga: RefreshManga, 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 getCategories: GetCategories,
private val getMangaCategories: GetMangaCategories, private val getMangaCategories: GetMangaCategories,
private val addMangaToCategory: AddMangaToCategory, private val addMangaToCategory: AddMangaToCategory,
@@ -138,23 +146,31 @@ class MangaScreenViewModel @Inject constructor(
} }
if (manga != null) { if (manga != null) {
_manga.value = manga _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 { private suspend fun refreshChaptersAsync(mangaId: Long, refresh: Boolean = false) = withIOContext {
async { async {
_chapters.value = chapterHandler.getChapters(mangaId, refresh) val chapters = if (refresh) {
.catch { refreshChapters.await(mangaId)
log.warn(it) { "Error getting chapters" } } else {
emit(emptyList()) getChapters.await(mangaId)
} }
.single() if (chapters != null) {
.toDownloadChapters() _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) { fun toggleRead(index: Int) {
val chapter = findChapter(index) ?: return
scope.launch { scope.launch {
manga.value?.let { manga -> manga.value?.let { manga ->
chapterHandler.updateChapter( updateChapterFlags.await(manga, index, read = chapter.read.not())
manga.id, refreshChaptersAsync(manga.id).await()
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()
} }
} }
} }
fun toggleBookmarked(index: Int) { fun toggleBookmarked(index: Int) {
val chapter = findChapter(index) ?: return
scope.launch { scope.launch {
manga.value?.let { manga -> manga.value?.let { manga ->
chapterHandler.updateChapter( updateChapterFlags.await(manga, index, bookmarked = chapter.bookmarked.not())
manga.id, refreshChaptersAsync(manga.id).await()
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()
} }
} }
} }
@@ -242,48 +234,30 @@ class MangaScreenViewModel @Inject constructor(
fun markPreviousRead(index: Int) { fun markPreviousRead(index: Int) {
scope.launch { scope.launch {
manga.value?.let { manga -> manga.value?.let { manga ->
chapterHandler.updateChapter(manga.id, index, markPreviousRead = true) updateChapterFlags.await(manga, index, markPreviousRead = true)
.catch { refreshChaptersAsync(manga.id).await()
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()
} }
} }
} }
fun downloadChapter(index: Int) { fun downloadChapter(index: Int) {
manga.value?.let { manga -> manga.value?.let { manga ->
chapterHandler.queueChapterDownload(manga.id, index) scope.launch { queueChapterDownload.await(manga, index) }
.catch {
log.warn(it) { "Error downloading chapter" }
}
.launchIn(scope)
} }
} }
fun deleteDownload(index: Int) { fun deleteDownload(index: Int) {
chapters.value.find { it.chapter.index == index } scope.launch {
?.deleteDownload(chapterHandler) chapters.value.find { it.chapter.index == index }
?.catch { ?.deleteDownload(deleteChapterDownload)
log.warn(it) { "Error deleting download" } }
}
?.launchIn(scope)
} }
fun stopDownloadingChapter(index: Int) { fun stopDownloadingChapter(index: Int) {
chapters.value.find { it.chapter.index == index } scope.launch {
?.stopDownloading(chapterHandler) chapters.value.find { it.chapter.index == index }
?.catch { ?.stopDownloading(stopChapterDownload)
log.warn(it) { "Error stopping download" } }
}
?.launchIn(scope)
} }
private fun List<Chapter>.toDownloadChapters() = map { private fun List<Chapter>.toDownloadChapters() = map {

View File

@@ -6,7 +6,7 @@
package ca.gosyer.jui.ui.reader 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.domain.reader.service.ReaderPreferences
import ca.gosyer.jui.ui.reader.loader.TachideskPageLoader import ca.gosyer.jui.ui.reader.loader.TachideskPageLoader
import ca.gosyer.jui.ui.reader.model.ReaderChapter import ca.gosyer.jui.ui.reader.model.ReaderChapter
@@ -20,7 +20,7 @@ import org.lighthousegames.logging.logging
class ChapterLoader( class ChapterLoader(
private val readerPreferences: ReaderPreferences, private val readerPreferences: ReaderPreferences,
private val chapterHandler: ChapterRepositoryImpl private val getChapterPage: GetChapterPage
) { ) {
fun loadChapter(chapter: ReaderChapter): StateFlow<List<ReaderPage>> { fun loadChapter(chapter: ReaderChapter): StateFlow<List<ReaderPage>> {
if (chapterIsReady(chapter)) { if (chapterIsReady(chapter)) {
@@ -29,7 +29,7 @@ class ChapterLoader(
chapter.state = ReaderChapter.State.Loading chapter.state = ReaderChapter.State.Loading
log.debug { "Loading pages for ${chapter.chapter.name}" } log.debug { "Loading pages for ${chapter.chapter.name}" }
val loader = TachideskPageLoader(chapter, readerPreferences, chapterHandler) val loader = TachideskPageLoader(chapter, readerPreferences, getChapterPage)
val pages = loader.getPages() 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.lang.launchDefault
import ca.gosyer.jui.core.prefs.getAsFlow 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.interactor.UpdateChapterMeta
import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.manga.interactor.GetManga import ca.gosyer.jui.domain.manga.interactor.GetManga
@@ -51,7 +54,10 @@ import org.lighthousegames.logging.logging
class ReaderMenuViewModel @Inject constructor( class ReaderMenuViewModel @Inject constructor(
private val readerPreferences: ReaderPreferences, private val readerPreferences: ReaderPreferences,
private val getManga: GetManga, 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 updateMangaMeta: UpdateMangaMeta,
private val updateChapterMeta: UpdateChapterMeta, private val updateChapterMeta: UpdateChapterMeta,
contextWrapper: ContextWrapper, contextWrapper: ContextWrapper,
@@ -101,7 +107,7 @@ class ReaderMenuViewModel @Inject constructor(
val readerModeSettings = ReaderModeWatch(readerPreferences, scope, readerMode) val readerModeSettings = ReaderModeWatch(readerPreferences, scope, readerMode)
private val loader = ChapterLoader(readerPreferences, chapterHandler) private val loader = ChapterLoader(readerPreferences, getChapterPage)
init { init {
init() init()
@@ -217,7 +223,7 @@ class ReaderMenuViewModel @Inject constructor(
private suspend fun initChapters(mangaId: Long, chapterIndex: Int) { private suspend fun initChapters(mangaId: Long, chapterIndex: Int) {
resetValues() resetValues()
val chapter = ReaderChapter( val chapter = ReaderChapter(
chapterHandler.getChapter(mangaId, chapterIndex) getChapter.asFlow(mangaId, chapterIndex)
.catch { .catch {
_state.value = ReaderChapter.State.Error(it) _state.value = ReaderChapter.State.Error(it)
log.warn(it) { "Error getting chapter" } log.warn(it) { "Error getting chapter" }
@@ -227,9 +233,10 @@ class ReaderMenuViewModel @Inject constructor(
val pages = loader.loadChapter(chapter) val pages = loader.loadChapter(chapter)
viewerChapters.currChapter.value = chapter viewerChapters.currChapter.value = chapter
scope.launchDefault { scope.launchDefault {
val chapters = chapterHandler.getChapters(mangaId) val chapters = getChapters.asFlow(mangaId)
.catch { .catch {
log.warn(it) { "Error getting chapter list" } log.warn(it) { "Error getting chapter list" }
// TODO: 2022-07-01 Error toast
emit(emptyList()) emit(emptyList())
} }
.single() .single()
@@ -273,29 +280,21 @@ class ReaderMenuViewModel @Inject constructor(
.onEach { index -> .onEach { index ->
pages.value.getOrNull(_currentPage.value - 1)?.let { chapter.pageLoader?.loadPage(it) } pages.value.getOrNull(_currentPage.value - 1)?.let { chapter.pageLoader?.loadPage(it) }
if (index == pages.value.size) { if (index == pages.value.size) {
markChapterRead(mangaId, chapter) markChapterRead(chapter)
} }
} }
.launchIn(chapter.scope) .launchIn(chapter.scope)
} }
private fun markChapterRead(mangaId: Long, chapter: ReaderChapter) { private fun markChapterRead(chapter: ReaderChapter) {
chapterHandler.updateChapter(mangaId, chapter.chapter.index, true) scope.launch { updateChapterFlags.await(chapter.chapter, read = true) }
.catch {
log.warn(it) { "Error marking chapter read" }
}
.launchIn(scope)
} }
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
fun sendProgress(chapter: Chapter? = this.chapter.value?.chapter, lastPageRead: Int = currentPage.value) { fun sendProgress(chapter: Chapter? = this.chapter.value?.chapter, lastPageRead: Int = currentPage.value) {
chapter ?: return chapter ?: return
if (chapter.read) return if (chapter.read) return
chapterHandler.updateChapter(chapter.mangaId, chapter.index, lastPageRead = lastPageRead) GlobalScope.launch { updateChapterFlags.await(chapter, lastPageRead = lastPageRead) }
.catch {
log.warn(it) { "Error sending progress" }
}
.launchIn(GlobalScope)
} }
fun updateLastPageReadOffset(offset: Int) { 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.IO
import ca.gosyer.jui.core.lang.throwIfCancellation 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.domain.reader.service.ReaderPreferences
import ca.gosyer.jui.ui.reader.model.ReaderChapter import ca.gosyer.jui.ui.reader.model.ReaderChapter
import ca.gosyer.jui.ui.reader.model.ReaderPage import ca.gosyer.jui.ui.reader.model.ReaderPage
@@ -33,7 +33,7 @@ import org.lighthousegames.logging.logging
class TachideskPageLoader( class TachideskPageLoader(
val chapter: ReaderChapter, val chapter: ReaderChapter,
readerPreferences: ReaderPreferences, readerPreferences: ReaderPreferences,
chapterHandler: ChapterRepositoryImpl getChapterPage: GetChapterPage
) : PageLoader() { ) : PageLoader() {
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
@@ -63,8 +63,7 @@ class TachideskPageLoader(
val page = priorityPage.page val page = priorityPage.page
log.debug { "Loading page ${page.index}" } log.debug { "Loading page ${page.index}" }
if (page.status.value == ReaderPage.Status.QUEUE) { if (page.status.value == ReaderPage.Status.QUEUE) {
chapterHandler getChapterPage.asFlow(chapter.chapter, page.index) {
.getPage(chapter.chapter.mangaId, chapter.chapter.index, page.index) {
onDownload { bytesSentTotal, contentLength -> onDownload { bytesSentTotal, contentLength ->
page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F) page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F)
} }
@@ -78,7 +77,7 @@ class TachideskPageLoader(
page.bitmap.value = null page.bitmap.value = null
page.status.value = ReaderPage.Status.ERROR page.status.value = ReaderPage.Status.ERROR
page.error.value = it.message 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) .flowOn(Dispatchers.IO)
.collect() .collect()

View File

@@ -10,7 +10,9 @@ import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.snapshots.SnapshotStateList 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.chapter.model.Chapter
import ca.gosyer.jui.domain.download.service.DownloadService import ca.gosyer.jui.domain.download.service.DownloadService
import ca.gosyer.jui.domain.updates.interactor.GetRecentUpdates import ca.gosyer.jui.domain.updates.interactor.GetRecentUpdates
@@ -37,7 +39,9 @@ import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging import org.lighthousegames.logging.logging
class UpdatesScreenViewModel @Inject constructor( class UpdatesScreenViewModel @Inject constructor(
private val chapterHandler: ChapterRepositoryImpl, private val queueChapterDownload: QueueChapterDownload,
private val stopChapterDownload: StopChapterDownload,
private val deleteChapterDownload: DeleteChapterDownload,
private val getRecentUpdates: GetRecentUpdates, private val getRecentUpdates: GetRecentUpdates,
contextWrapper: ContextWrapper contextWrapper: ContextWrapper
) : ViewModel(contextWrapper) { ) : ViewModel(contextWrapper) {
@@ -117,41 +121,33 @@ class UpdatesScreenViewModel @Inject constructor(
} }
fun downloadChapter(chapter: Chapter) { fun downloadChapter(chapter: Chapter) {
chapterHandler.queueChapterDownload(chapter.mangaId, chapter.index) scope.launch { queueChapterDownload.await(chapter) }
.catch {
log.warn(it) { "Error queueing chapter" }
}
.launchIn(scope)
} }
fun deleteDownloadedChapter(chapter: Chapter) { fun deleteDownloadedChapter(chapter: Chapter) {
_updates scope.launch {
.firstNotNullOfOrNull { (_, chapters) -> _updates
chapters.find { .firstNotNullOfOrNull { (_, chapters) ->
it.chapter.mangaId == chapter.mangaId && chapters.find {
it.chapter.index == chapter.index it.chapter.mangaId == chapter.mangaId &&
it.chapter.index == chapter.index
}
} }
} ?.deleteDownload(deleteChapterDownload)
?.deleteDownload(chapterHandler) }
?.catch {
log.warn(it) { "Error deleting download" }
}
?.launchIn(scope)
} }
fun stopDownloadingChapter(chapter: Chapter) { fun stopDownloadingChapter(chapter: Chapter) {
_updates scope.launch {
.firstNotNullOfOrNull { (_, chapters) -> _updates
chapters.find { .firstNotNullOfOrNull { (_, chapters) ->
it.chapter.mangaId == chapter.mangaId && chapters.find {
it.chapter.index == chapter.index it.chapter.mangaId == chapter.mangaId &&
it.chapter.index == chapter.index
}
} }
} ?.stopDownloading(stopChapterDownload)
?.stopDownloading(chapterHandler) }
?.catch {
log.warn(it) { "Error stopping download" }
}
?.launchIn(scope)
} }
private companion object { private companion object {