Use interactors for category and library data calls

This commit is contained in:
Syer10
2022-07-01 11:19:17 -04:00
parent 20a2262638
commit ca2e3813bb
18 changed files with 470 additions and 160 deletions

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.category.interactor
import ca.gosyer.jui.domain.category.model.Category
import ca.gosyer.jui.domain.category.service.CategoryRepository
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 AddMangaToCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(mangaId: Long, categoryId: Long) = asFlow(mangaId, categoryId)
.catch { log.warn(it) { "Failed to add $mangaId to category $categoryId" } }
.collect()
suspend fun await(manga: Manga, category: Category) = asFlow(manga, category)
.catch { log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to category ${category.name}" } }
.collect()
fun asFlow(mangaId: Long, categoryId: Long) = categoryRepository.addMangaToCategory(mangaId, categoryId)
fun asFlow(manga: Manga, category: Category) = categoryRepository.addMangaToCategory(manga.id, category.id)
companion object {
private val log = logging()
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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.category.interactor
import ca.gosyer.jui.domain.category.service.CategoryRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class CreateCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(name: String) = asFlow(name)
.catch { log.warn(it) { "Failed to create category $name" } }
.collect()
fun asFlow(name: String) = categoryRepository.createCategory(name)
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.category.interactor
import ca.gosyer.jui.domain.category.model.Category
import ca.gosyer.jui.domain.category.service.CategoryRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class DeleteCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(categoryId: Long) = asFlow(categoryId)
.catch { log.warn(it) { "Failed to delete category $categoryId" } }
.collect()
suspend fun await(category: Category) = asFlow(category)
.catch { log.warn(it) { "Failed to delete category ${category.name}" } }
.collect()
fun asFlow(categoryId: Long) = categoryRepository.deleteCategory(categoryId)
fun asFlow(category: Category) = categoryRepository.deleteCategory(category.id)
companion object {
private val log = logging()
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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.category.interactor
import ca.gosyer.jui.domain.category.service.CategoryRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetCategories @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(dropDefault: Boolean = false) = asFlow(dropDefault)
.catch { log.warn(it) { "Failed to get categories" } }
.singleOrNull()
fun asFlow(dropDefault: Boolean = false) = categoryRepository.getCategories(dropDefault)
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.category.interactor
import ca.gosyer.jui.domain.category.service.CategoryRepository
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 GetMangaCategories @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(mangaId: Long) = asFlow(mangaId)
.catch { log.warn(it) { "Failed to get categories for $mangaId" } }
.singleOrNull()
suspend fun await(manga: Manga) = asFlow(manga)
.catch { log.warn(it) { "Failed to get categories for ${manga.title}(${manga.id})" } }
.singleOrNull()
fun asFlow(mangaId: Long) = categoryRepository.getMangaCategories(mangaId)
fun asFlow(manga: Manga) = categoryRepository.getMangaCategories(manga.id)
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.category.interactor
import ca.gosyer.jui.domain.category.model.Category
import ca.gosyer.jui.domain.category.service.CategoryRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetMangaListFromCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(categoryId: Long) = asFlow(categoryId)
.catch { log.warn(it) { "Failed to get manga list from category $categoryId" } }
.singleOrNull()
suspend fun await(category: Category) = asFlow(category)
.catch { log.warn(it) { "Failed to get manga list from category ${category.name}" } }
.singleOrNull()
fun asFlow(categoryId: Long) = categoryRepository.getMangaFromCategory(categoryId)
fun asFlow(category: Category) = categoryRepository.getMangaFromCategory(category.id)
companion object {
private val log = logging()
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.category.interactor
import ca.gosyer.jui.domain.category.model.Category
import ca.gosyer.jui.domain.category.service.CategoryRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ModifyCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = asFlow(
categoryId = categoryId,
name = name,
isLanding = isLanding
).catch { log.warn(it) { "Failed to modify category $categoryId with options: name=$name,isLanding=$isLanding" } }.collect()
suspend fun await(category: Category, name: String? = null, isLanding: Boolean? = null) = asFlow(
category = category,
name = name,
isLanding = isLanding
).catch { log.warn(it) { "Failed to modify category ${category.name} with options: name=$name,isLanding=$isLanding" } }.collect()
fun asFlow(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = categoryRepository.modifyCategory(
categoryId = categoryId,
name = name,
isLanding = isLanding
)
fun asFlow(category: Category, name: String? = null, isLanding: Boolean? = null) = categoryRepository.modifyCategory(
categoryId = category.id,
name = name,
isLanding = isLanding
)
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.category.interactor
import ca.gosyer.jui.domain.category.model.Category
import ca.gosyer.jui.domain.category.service.CategoryRepository
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 RemoveMangaFromCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(mangaId: Long, categoryId: Long) = asFlow(mangaId, categoryId)
.catch { log.warn(it) { "Failed to remove $mangaId from category $categoryId" } }
.collect()
suspend fun await(manga: Manga, category: Category) = asFlow(manga, category)
.catch { log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from category ${category.name}" } }
.collect()
fun asFlow(mangaId: Long, categoryId: Long) = categoryRepository.removeMangaFromCategory(mangaId, categoryId)
fun asFlow(manga: Manga, category: Category) = categoryRepository.removeMangaFromCategory(manga.id, category.id)
companion object {
private val log = logging()
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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.category.interactor
import ca.gosyer.jui.domain.category.service.CategoryRepository
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ReorderCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
suspend fun await(to: Int, from: Int) = asFlow(to, from)
.catch { log.warn(it) { "Failed to move category from $from to $to" } }
.collect()
fun asFlow(to: Int, from: Int) = categoryRepository.reorderCategory(to, from)
companion object {
private val log = logging()
}
}

View File

@@ -8,25 +8,37 @@ package ca.gosyer.jui.domain.chapter.interactor
import ca.gosyer.jui.domain.chapter.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterMeta @Inject constructor(private val chapterRepository: ChapterRepository) {
fun subscribe(
suspend fun await(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset
) = asFlow(chapter, pageOffset)
.catch { log.warn(it) { "Failed to update ${chapter.name}(${chapter.index}) meta" } }
.collect()
fun asFlow(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset
) = flow {
if (pageOffset != chapter.meta.juiPageOffset) {
emitAll(
chapterRepository.updateChapterMeta(
chapter.mangaId,
chapter.index,
"juiPageOffset",
pageOffset.toString()
)
)
chapterRepository.updateChapterMeta(
chapter.mangaId,
chapter.index,
"juiPageOffset",
pageOffset.toString()
).collect()
}
emit(Unit)
}
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.library.interactor
import ca.gosyer.jui.domain.library.service.LibraryRepository
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 AddMangaToLibrary @Inject constructor(private val libraryRepository: LibraryRepository) {
suspend fun await(mangaId: Long) = asFlow(mangaId)
.catch { log.warn(it) { "Failed to add $mangaId to library" } }
.singleOrNull()
suspend fun await(manga: Manga) = asFlow(manga)
.catch { log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to library" } }
.singleOrNull()
fun asFlow(mangaId: Long) = libraryRepository.addMangaToLibrary(mangaId)
fun asFlow(manga: Manga) = libraryRepository.addMangaToLibrary(manga.id)
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.library.interactor
import ca.gosyer.jui.domain.library.service.LibraryRepository
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 RemoveMangaFromLibrary @Inject constructor(private val libraryRepository: LibraryRepository) {
suspend fun await(mangaId: Long) = asFlow(mangaId)
.catch { log.warn(it) { "Failed to remove $mangaId from library" } }
.singleOrNull()
suspend fun await(manga: Manga) = asFlow(manga)
.catch { log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from library" } }
.singleOrNull()
fun asFlow(mangaId: Long) = libraryRepository.removeMangaFromLibrary(mangaId)
fun asFlow(manga: Manga) = libraryRepository.removeMangaFromLibrary(manga.id)
companion object {
private val log = logging()
}
}

View File

@@ -8,24 +8,36 @@ package ca.gosyer.jui.domain.manga.interactor
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.manga.service.MangaRepository
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateMangaMeta @Inject constructor(private val mangaRepository: MangaRepository) {
fun subscribe(
suspend fun await(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode
) = asFlow(manga, readerMode)
.catch { log.warn(it) { "Failed to update ${manga.title}(${manga.id}) meta" } }
.collect()
fun asFlow(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode
) = flow {
if (readerMode != manga.meta.juiReaderMode) {
emitAll(
mangaRepository.updateMangaMeta(
manga.id,
"juiReaderMode",
readerMode
)
)
mangaRepository.updateMangaMeta(
manga.id,
"juiReaderMode",
readerMode
).collect()
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

@@ -6,99 +6,71 @@
package ca.gosyer.jui.ui.categories
import ca.gosyer.jui.data.category.CategoryRepositoryImpl
import ca.gosyer.jui.domain.category.interactor.CreateCategory
import ca.gosyer.jui.domain.category.interactor.DeleteCategory
import ca.gosyer.jui.domain.category.interactor.GetCategories
import ca.gosyer.jui.domain.category.interactor.ModifyCategory
import ca.gosyer.jui.domain.category.interactor.ReorderCategory
import ca.gosyer.jui.domain.category.model.Category
import ca.gosyer.jui.uicore.vm.ContextWrapper
import ca.gosyer.jui.uicore.vm.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
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.flow.singleOrNull
import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class CategoriesScreenViewModel @Inject constructor(
private val categoryHandler: CategoryRepositoryImpl,
private val getCategories: GetCategories,
private val createCategory: CreateCategory,
private val deleteCategory: DeleteCategory,
private val modifyCategory: ModifyCategory,
private val reorderCategory: ReorderCategory,
contextWrapper: ContextWrapper
) : ViewModel(contextWrapper) {
private var originalCategories = emptyList<Category>()
private val _categories = MutableStateFlow(emptyList<MenuCategory>())
val categories = _categories.asStateFlow()
private val _isLoading = MutableStateFlow(true)
val isLoading = _isLoading.asStateFlow()
init {
getCategories()
scope.launch {
getCategories()
}
}
private fun getCategories() {
private suspend fun getCategories() {
_categories.value = emptyList()
_isLoading.value = true
categoryHandler.getCategories(true)
.onEach {
_categories.value = it
.sortedBy { it.order }
.also { originalCategories = it }
.map { it.toMenuCategory() }
_isLoading.value = false
}
.catch {
log.warn(it) { "Error getting categories" }
_isLoading.value = false
}
.launchIn(scope)
val categories = getCategories.await(true)
if (categories != null) {
_categories.value = categories
.sortedBy { it.order }
.also { originalCategories = it }
.map { it.toMenuCategory() }
}
}
suspend fun updateRemoteCategories(manualUpdate: Boolean = false) {
val categories = _categories.value
val newCategories = categories.filter { it.id == null }
newCategories.forEach {
categoryHandler.createCategory(it.name)
.catch {
log.warn(it) { "Error creating category" }
}
.collect()
createCategory.await(it.name)
}
originalCategories.forEach { originalCategory ->
val category = categories.find { it.id == originalCategory.id }
if (category == null) {
categoryHandler.deleteCategory(originalCategory.id)
.catch {
log.warn(it) { "Error deleting category $originalCategory" }
}
.collect()
deleteCategory.await(originalCategory)
} else if (category.name != originalCategory.name) {
categoryHandler.modifyCategory(originalCategory.id, category.name)
.catch {
log.warn(it) { "Error modifying category $category" }
}
.collect()
modifyCategory.await(originalCategory, category.name)
}
}
var updatedCategories = categoryHandler.getCategories(true)
.catch {
log.warn(it) { "Error getting updated categories" }
}
.singleOrNull()
var updatedCategories = getCategories.await(true)
categories.forEach { category ->
val updatedCategory = updatedCategories?.find { it.id == category.id || it.name == category.name } ?: return@forEach
if (category.order != updatedCategory.order) {
log.debug { "${category.name}: ${updatedCategory.order} to ${category.order}" }
categoryHandler.reorderCategory(category.order, updatedCategory.order)
.catch {
log.warn(it) { "Error re-ordering categories" }
}
.singleOrNull()
reorderCategory.await(category.order, updatedCategory.order)
}
updatedCategories = categoryHandler.getCategories(true)
.catch {
log.warn(it) { "Error getting updated categories" }
}
.singleOrNull()
updatedCategories = getCategories.await(true)
}
if (manualUpdate) {

View File

@@ -10,10 +10,11 @@ import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toLowerCase
import ca.gosyer.jui.core.lang.withDefaultContext
import ca.gosyer.jui.core.prefs.getAsFlow
import ca.gosyer.jui.data.category.CategoryRepositoryImpl
import ca.gosyer.jui.data.library.LibraryRepositoryImpl
import ca.gosyer.jui.data.updates.UpdatesRepositoryImpl
import ca.gosyer.jui.domain.category.interactor.GetCategories
import ca.gosyer.jui.domain.category.interactor.GetMangaListFromCategory
import ca.gosyer.jui.domain.category.model.Category
import ca.gosyer.jui.domain.library.interactor.RemoveMangaFromLibrary
import ca.gosyer.jui.domain.library.model.FilterState
import ca.gosyer.jui.domain.library.model.Sort
import ca.gosyer.jui.domain.library.service.LibraryPreferences
@@ -40,6 +41,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
@@ -74,8 +76,9 @@ private fun LibraryMap.setManga(id: Long, manga: List<Manga>, getItemsFlow: (Sta
}
class LibraryScreenViewModel @Inject constructor(
private val categoryHandler: CategoryRepositoryImpl,
private val libraryHandler: LibraryRepositoryImpl,
private val getCategories: GetCategories,
private val getMangaListFromCategory: GetMangaListFromCategory,
private val removeMangaFromLibrary: RemoveMangaFromLibrary,
private val updatesHandler: UpdatesRepositoryImpl,
libraryPreferences: LibraryPreferences,
contextWrapper: ContextWrapper
@@ -141,7 +144,7 @@ class LibraryScreenViewModel @Inject constructor(
private fun getLibrary() {
_isLoading.value = true
categoryHandler.getCategories()
getCategories.asFlow()
.onEach { categories ->
if (categories.isEmpty()) {
throw Exception(MR.strings.library_empty.toPlatformString())
@@ -152,7 +155,7 @@ class LibraryScreenViewModel @Inject constructor(
}
.catch {
_error.value = it.message
log.warn(it) { "Error getting categories" }
log.warn(it) { "Failed to get categories" }
_isLoading.value = false
}
.launchIn(scope)
@@ -237,7 +240,7 @@ class LibraryScreenViewModel @Inject constructor(
withDefaultContext {
categories.map { category ->
async {
categoryHandler.getMangaFromCategory(category.id)
getMangaListFromCategory.asFlow(category)
.onEach {
library.mangaMap.setManga(
id = category.id,
@@ -246,7 +249,7 @@ class LibraryScreenViewModel @Inject constructor(
)
}
.catch {
log.warn(it) { "Error getting manga for category $category" }
log.warn(it) { "Failed to get manga list from category ${category.name}" }
library.mangaMap.setError(category.id, it)
}
.collect()
@@ -264,14 +267,9 @@ class LibraryScreenViewModel @Inject constructor(
}
fun removeManga(mangaId: Long) {
libraryHandler.removeMangaFromLibrary(mangaId)
.onEach {
updateCategories(getCategoriesToUpdate(mangaId))
}
.catch {
log.warn(it) { "Error removing manga from library" }
}
.launchIn(scope)
scope.launch {
removeMangaFromLibrary.await(mangaId)
}
}
fun updateQuery(query: String) {

View File

@@ -8,13 +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.category.CategoryRepositoryImpl
import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl
import ca.gosyer.jui.data.library.LibraryRepositoryImpl
import ca.gosyer.jui.data.manga.MangaRepositoryImpl
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.model.Chapter
import ca.gosyer.jui.domain.download.service.DownloadService
import ca.gosyer.jui.domain.library.interactor.AddMangaToLibrary
import ca.gosyer.jui.domain.library.interactor.RemoveMangaFromLibrary
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.ui.service.UiPreferences
import ca.gosyer.jui.ui.base.chapter.ChapterDownloadItem
@@ -41,8 +45,12 @@ class MangaScreenViewModel @Inject constructor(
private val dateHandler: DateHandler,
private val mangaHandler: MangaRepositoryImpl,
private val chapterHandler: ChapterRepositoryImpl,
private val categoryHandler: CategoryRepositoryImpl,
private val libraryHandler: LibraryRepositoryImpl,
private val getCategories: GetCategories,
private val getMangaCategories: GetMangaCategories,
private val addMangaToCategory: AddMangaToCategory,
private val removeMangaFromCategory: RemoveMangaFromCategory,
private val addMangaToLibrary: AddMangaToLibrary,
private val removeMangaFromLibrary: RemoveMangaFromLibrary,
uiPreferences: UiPreferences,
contextWrapper: ContextWrapper,
private val params: Params,
@@ -56,8 +64,9 @@ class MangaScreenViewModel @Inject constructor(
private val _isLoading = MutableStateFlow(true)
val isLoading = _isLoading.asStateFlow()
private val _categories = MutableStateFlow(emptyList<Category>())
val categories = _categories.asStateFlow()
val categories = getCategories.asFlow(true)
.catch { log.warn(it) { "Failed to get categories" } }
.stateIn(scope, SharingStarted.Eagerly, emptyList())
private val _mangaCategories = MutableStateFlow(emptyList<Category>())
val mangaCategories = _mangaCategories.asStateFlow()
@@ -86,15 +95,6 @@ class MangaScreenViewModel @Inject constructor(
refreshMangaAsync(params.mangaId).await() to refreshChaptersAsync(params.mangaId).await()
_isLoading.value = false
}
categoryHandler.getCategories(true)
.onEach {
_categories.value = it
}
.catch {
log.warn(it) { "Error getting categories" }
}
.launchIn(scope)
}
fun loadManga() {
@@ -138,14 +138,10 @@ class MangaScreenViewModel @Inject constructor(
log.warn(it) { "Error getting manga" }
}
.collect()
categoryHandler.getMangaCategories(mangaId)
.onEach {
getMangaCategories.await(mangaId)
?.let {
_mangaCategories.value = it
}
.catch {
log.warn(it) { "Error getting manga" }
}
.collect()
}
}
@@ -165,11 +161,7 @@ class MangaScreenViewModel @Inject constructor(
scope.launch {
manga.value?.let { manga ->
if (manga.inLibrary) {
libraryHandler.removeMangaFromLibrary(manga.id)
.catch {
log.warn(it) { "Error toggling favorite" }
}
.collect()
removeMangaFromLibrary.await(manga)
refreshMangaAsync(manga.id).await()
} else {
if (categories.value.isEmpty()) {
@@ -187,25 +179,13 @@ class MangaScreenViewModel @Inject constructor(
manga.value?.let { manga ->
if (manga.inLibrary) {
oldCategories.filterNot { it in categories }.forEach {
categoryHandler.removeMangaFromCategory(manga.id, it.id)
.catch {
log.warn(it) { "Error removing manga from category" }
}
.collect()
removeMangaFromCategory.await(manga, it)
}
} else {
libraryHandler.addMangaToLibrary(manga.id)
.catch {
log.warn(it) { "Error Adding manga to library" }
}
.collect()
addMangaToLibrary.await(manga)
}
categories.filterNot { it in oldCategories }.forEach {
categoryHandler.addMangaToCategory(manga.id, it.id)
.catch {
log.warn(it) { "Error adding manga to category" }
}
.collect()
addMangaToCategory.await(manga, it)
}
refreshMangaAsync(manga.id).await()
}

View File

@@ -166,11 +166,7 @@ class ReaderMenuViewModel @Inject constructor(
fun setMangaReaderMode(mode: String) {
scope.launchDefault {
_manga.value?.let {
updateMangaMeta.subscribe(it, mode)
.catch {
log.warn(it) { "Error updating manga reader mode" }
}
.collect()
updateMangaMeta.await(it, mode)
}
initManga(params.mangaId)
}
@@ -308,11 +304,7 @@ class ReaderMenuViewModel @Inject constructor(
@OptIn(DelicateCoroutinesApi::class)
private fun updateLastPageReadOffset(chapter: Chapter, offset: Int) {
updateChapterMeta.subscribe(chapter, offset)
.catch {
log.warn(it) { "Error updating chapter offset" }
}
.launchIn(GlobalScope)
GlobalScope.launch { updateChapterMeta.await(chapter, offset) }
}
override fun onDispose() {

View File

@@ -33,7 +33,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import ca.gosyer.jui.data.category.CategoryRepositoryImpl
import ca.gosyer.jui.domain.category.interactor.GetCategories
import ca.gosyer.jui.domain.library.model.DisplayMode
import ca.gosyer.jui.domain.library.service.LibraryPreferences
import ca.gosyer.jui.i18n.MR
@@ -59,11 +59,8 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState
import com.vanpra.composematerialdialogs.title
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
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
import kotlin.math.roundToInt
class SettingsLibraryScreen : Screen {
@@ -88,7 +85,7 @@ class SettingsLibraryScreen : Screen {
class SettingsLibraryViewModel @Inject constructor(
libraryPreferences: LibraryPreferences,
private val categoryHandler: CategoryRepositoryImpl,
private val getCategories: GetCategories,
contextWrapper: ContextWrapper
) : ViewModel(contextWrapper) {
@@ -105,23 +102,14 @@ class SettingsLibraryViewModel @Inject constructor(
}
fun refreshCategoryCount() {
categoryHandler.getCategories(true)
.onEach {
_categories.value = it.size
}
.catch {
log.warn(it) { "Error getting categories" }
}
.launchIn(scope)
scope.launch {
_categories.value = getCategories.await(true)?.size ?: 0
}
}
@Composable
fun getDisplayModeChoices() = DisplayMode.values()
.associateWith { stringResource(it.res) }
private companion object {
private val log = logging()
}
}
@Composable