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.model.Chapter
import ca.gosyer.jui.domain.chapter.service.ChapterRepository 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 kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterMeta @Inject constructor(private val chapterRepository: ChapterRepository) { 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, chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset pageOffset: Int = chapter.meta.juiPageOffset
) = flow { ) = flow {
if (pageOffset != chapter.meta.juiPageOffset) { if (pageOffset != chapter.meta.juiPageOffset) {
emitAll( chapterRepository.updateChapterMeta(
chapterRepository.updateChapterMeta( chapter.mangaId,
chapter.mangaId, chapter.index,
chapter.index, "juiPageOffset",
"juiPageOffset", pageOffset.toString()
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.model.Manga
import ca.gosyer.jui.domain.manga.service.MangaRepository 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 kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateMangaMeta @Inject constructor(private val mangaRepository: MangaRepository) { 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, manga: Manga,
readerMode: String = manga.meta.juiReaderMode readerMode: String = manga.meta.juiReaderMode
) = flow { ) = flow {
if (readerMode != manga.meta.juiReaderMode) { if (readerMode != manga.meta.juiReaderMode) {
emitAll( mangaRepository.updateMangaMeta(
mangaRepository.updateMangaMeta( manga.id,
manga.id, "juiReaderMode",
"juiReaderMode", readerMode
readerMode ).collect()
)
)
} }
emit(Unit)
}
companion object {
private val log = logging()
} }
} }

View File

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

View File

@@ -10,10 +10,11 @@ import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toLowerCase import androidx.compose.ui.text.toLowerCase
import ca.gosyer.jui.core.lang.withDefaultContext import ca.gosyer.jui.core.lang.withDefaultContext
import ca.gosyer.jui.core.prefs.getAsFlow 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.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.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.FilterState
import ca.gosyer.jui.domain.library.model.Sort import ca.gosyer.jui.domain.library.model.Sort
import ca.gosyer.jui.domain.library.service.LibraryPreferences 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.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
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
@@ -74,8 +76,9 @@ private fun LibraryMap.setManga(id: Long, manga: List<Manga>, getItemsFlow: (Sta
} }
class LibraryScreenViewModel @Inject constructor( class LibraryScreenViewModel @Inject constructor(
private val categoryHandler: CategoryRepositoryImpl, private val getCategories: GetCategories,
private val libraryHandler: LibraryRepositoryImpl, private val getMangaListFromCategory: GetMangaListFromCategory,
private val removeMangaFromLibrary: RemoveMangaFromLibrary,
private val updatesHandler: UpdatesRepositoryImpl, private val updatesHandler: UpdatesRepositoryImpl,
libraryPreferences: LibraryPreferences, libraryPreferences: LibraryPreferences,
contextWrapper: ContextWrapper contextWrapper: ContextWrapper
@@ -141,7 +144,7 @@ class LibraryScreenViewModel @Inject constructor(
private fun getLibrary() { private fun getLibrary() {
_isLoading.value = true _isLoading.value = true
categoryHandler.getCategories() getCategories.asFlow()
.onEach { categories -> .onEach { categories ->
if (categories.isEmpty()) { if (categories.isEmpty()) {
throw Exception(MR.strings.library_empty.toPlatformString()) throw Exception(MR.strings.library_empty.toPlatformString())
@@ -152,7 +155,7 @@ class LibraryScreenViewModel @Inject constructor(
} }
.catch { .catch {
_error.value = it.message _error.value = it.message
log.warn(it) { "Error getting categories" } log.warn(it) { "Failed to get categories" }
_isLoading.value = false _isLoading.value = false
} }
.launchIn(scope) .launchIn(scope)
@@ -237,7 +240,7 @@ class LibraryScreenViewModel @Inject constructor(
withDefaultContext { withDefaultContext {
categories.map { category -> categories.map { category ->
async { async {
categoryHandler.getMangaFromCategory(category.id) getMangaListFromCategory.asFlow(category)
.onEach { .onEach {
library.mangaMap.setManga( library.mangaMap.setManga(
id = category.id, id = category.id,
@@ -246,7 +249,7 @@ class LibraryScreenViewModel @Inject constructor(
) )
} }
.catch { .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) library.mangaMap.setError(category.id, it)
} }
.collect() .collect()
@@ -264,14 +267,9 @@ class LibraryScreenViewModel @Inject constructor(
} }
fun removeManga(mangaId: Long) { fun removeManga(mangaId: Long) {
libraryHandler.removeMangaFromLibrary(mangaId) scope.launch {
.onEach { removeMangaFromLibrary.await(mangaId)
updateCategories(getCategoriesToUpdate(mangaId)) }
}
.catch {
log.warn(it) { "Error removing manga from library" }
}
.launchIn(scope)
} }
fun updateQuery(query: String) { 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.core.lang.withIOContext
import ca.gosyer.jui.data.base.DateHandler 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.chapter.ChapterRepositoryImpl
import ca.gosyer.jui.data.library.LibraryRepositoryImpl
import ca.gosyer.jui.data.manga.MangaRepositoryImpl 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.category.model.Category
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.RemoveMangaFromLibrary
import ca.gosyer.jui.domain.manga.model.Manga import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.ui.service.UiPreferences import ca.gosyer.jui.domain.ui.service.UiPreferences
import ca.gosyer.jui.ui.base.chapter.ChapterDownloadItem import ca.gosyer.jui.ui.base.chapter.ChapterDownloadItem
@@ -41,8 +45,12 @@ class MangaScreenViewModel @Inject constructor(
private val dateHandler: DateHandler, private val dateHandler: DateHandler,
private val mangaHandler: MangaRepositoryImpl, private val mangaHandler: MangaRepositoryImpl,
private val chapterHandler: ChapterRepositoryImpl, private val chapterHandler: ChapterRepositoryImpl,
private val categoryHandler: CategoryRepositoryImpl, private val getCategories: GetCategories,
private val libraryHandler: LibraryRepositoryImpl, private val getMangaCategories: GetMangaCategories,
private val addMangaToCategory: AddMangaToCategory,
private val removeMangaFromCategory: RemoveMangaFromCategory,
private val addMangaToLibrary: AddMangaToLibrary,
private val removeMangaFromLibrary: RemoveMangaFromLibrary,
uiPreferences: UiPreferences, uiPreferences: UiPreferences,
contextWrapper: ContextWrapper, contextWrapper: ContextWrapper,
private val params: Params, private val params: Params,
@@ -56,8 +64,9 @@ class MangaScreenViewModel @Inject constructor(
private val _isLoading = MutableStateFlow(true) private val _isLoading = MutableStateFlow(true)
val isLoading = _isLoading.asStateFlow() val isLoading = _isLoading.asStateFlow()
private val _categories = MutableStateFlow(emptyList<Category>()) val categories = getCategories.asFlow(true)
val categories = _categories.asStateFlow() .catch { log.warn(it) { "Failed to get categories" } }
.stateIn(scope, SharingStarted.Eagerly, emptyList())
private val _mangaCategories = MutableStateFlow(emptyList<Category>()) private val _mangaCategories = MutableStateFlow(emptyList<Category>())
val mangaCategories = _mangaCategories.asStateFlow() val mangaCategories = _mangaCategories.asStateFlow()
@@ -86,15 +95,6 @@ class MangaScreenViewModel @Inject constructor(
refreshMangaAsync(params.mangaId).await() to refreshChaptersAsync(params.mangaId).await() refreshMangaAsync(params.mangaId).await() to refreshChaptersAsync(params.mangaId).await()
_isLoading.value = false _isLoading.value = false
} }
categoryHandler.getCategories(true)
.onEach {
_categories.value = it
}
.catch {
log.warn(it) { "Error getting categories" }
}
.launchIn(scope)
} }
fun loadManga() { fun loadManga() {
@@ -138,14 +138,10 @@ class MangaScreenViewModel @Inject constructor(
log.warn(it) { "Error getting manga" } log.warn(it) { "Error getting manga" }
} }
.collect() .collect()
categoryHandler.getMangaCategories(mangaId) getMangaCategories.await(mangaId)
.onEach { ?.let {
_mangaCategories.value = it _mangaCategories.value = it
} }
.catch {
log.warn(it) { "Error getting manga" }
}
.collect()
} }
} }
@@ -165,11 +161,7 @@ class MangaScreenViewModel @Inject constructor(
scope.launch { scope.launch {
manga.value?.let { manga -> manga.value?.let { manga ->
if (manga.inLibrary) { if (manga.inLibrary) {
libraryHandler.removeMangaFromLibrary(manga.id) removeMangaFromLibrary.await(manga)
.catch {
log.warn(it) { "Error toggling favorite" }
}
.collect()
refreshMangaAsync(manga.id).await() refreshMangaAsync(manga.id).await()
} else { } else {
if (categories.value.isEmpty()) { if (categories.value.isEmpty()) {
@@ -187,25 +179,13 @@ class MangaScreenViewModel @Inject constructor(
manga.value?.let { manga -> manga.value?.let { manga ->
if (manga.inLibrary) { if (manga.inLibrary) {
oldCategories.filterNot { it in categories }.forEach { oldCategories.filterNot { it in categories }.forEach {
categoryHandler.removeMangaFromCategory(manga.id, it.id) removeMangaFromCategory.await(manga, it)
.catch {
log.warn(it) { "Error removing manga from category" }
}
.collect()
} }
} else { } else {
libraryHandler.addMangaToLibrary(manga.id) addMangaToLibrary.await(manga)
.catch {
log.warn(it) { "Error Adding manga to library" }
}
.collect()
} }
categories.filterNot { it in oldCategories }.forEach { categories.filterNot { it in oldCategories }.forEach {
categoryHandler.addMangaToCategory(manga.id, it.id) addMangaToCategory.await(manga, it)
.catch {
log.warn(it) { "Error adding manga to category" }
}
.collect()
} }
refreshMangaAsync(manga.id).await() refreshMangaAsync(manga.id).await()
} }

View File

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

View File

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