mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Add ability to add manga to categories
This commit is contained in:
@@ -8,6 +8,7 @@ package ca.gosyer.ui.manga
|
||||
|
||||
import androidx.compose.foundation.VerticalScrollbar
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
@@ -28,10 +29,14 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollbarAdapter
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Checkbox
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Label
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -40,7 +45,10 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.BuildConfig
|
||||
import ca.gosyer.data.models.Category
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.WindowDialog
|
||||
import ca.gosyer.ui.base.components.ActionIcon
|
||||
import ca.gosyer.ui.base.components.ErrorScreen
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.LoadingScreen
|
||||
@@ -54,6 +62,8 @@ import ca.gosyer.util.compose.ThemedWindow
|
||||
import ca.gosyer.util.lang.launchApplication
|
||||
import com.github.zsoltk.compose.router.BackStack
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun openMangaMenu(mangaId: Long) {
|
||||
@@ -74,10 +84,30 @@ fun MangaMenu(mangaId: Long, backStack: BackStack<Route>? = null) {
|
||||
val isLoading by vm.isLoading.collectAsState()
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
val dateTimeFormatter by vm.dateTimeFormatter.collectAsState()
|
||||
val categoriesExist by vm.categoriesExist.collectAsState()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
vm.chooseCategoriesFlow.collect { (availableCategories, usedCategories) ->
|
||||
openCategorySelectDialog(availableCategories, usedCategories, vm::addFavorite)
|
||||
}
|
||||
}
|
||||
|
||||
Box {
|
||||
Column(Modifier.background(MaterialTheme.colors.background)) {
|
||||
Toolbar(stringResource("location_manga"), backStack, backStack != null)
|
||||
Toolbar(
|
||||
stringResource("location_manga"),
|
||||
backStack,
|
||||
backStack != null,
|
||||
actions = {
|
||||
if (categoriesExist) {
|
||||
ActionIcon(
|
||||
vm::setCategories,
|
||||
stringResource("edit_categories"),
|
||||
Icons.Rounded.Label
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
manga.let { manga ->
|
||||
if (manga != null) {
|
||||
@@ -200,3 +230,38 @@ private fun MangaInfo(manga: Manga, modifier: Modifier = Modifier) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun openCategorySelectDialog(
|
||||
categories: List<Category>,
|
||||
oldCategories: List<Category>,
|
||||
onPositiveClick: (List<Category>, List<Category>) -> Unit
|
||||
) {
|
||||
val enabledCategoriesFlow = MutableStateFlow(oldCategories)
|
||||
WindowDialog(
|
||||
"Select Categories",
|
||||
onPositiveButton = { onPositiveClick(enabledCategoriesFlow.value, oldCategories) }
|
||||
) {
|
||||
val enabledCategories by enabledCategoriesFlow.collectAsState()
|
||||
LazyColumn {
|
||||
items(categories) { category ->
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(8.dp)
|
||||
.clickable {
|
||||
if (category in enabledCategories) {
|
||||
enabledCategoriesFlow.value -= category
|
||||
} else {
|
||||
enabledCategoriesFlow.value += category
|
||||
}
|
||||
},
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(category.name, style = MaterialTheme.typography.subtitle1)
|
||||
Checkbox(
|
||||
category in enabledCategories,
|
||||
onCheckedChange = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@ package ca.gosyer.ui.manga
|
||||
|
||||
import ca.gosyer.data.download.DownloadService
|
||||
import ca.gosyer.data.download.model.DownloadChapter
|
||||
import ca.gosyer.data.models.Category
|
||||
import ca.gosyer.data.models.Chapter
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.interactions.CategoryInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.ChapterInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.LibraryInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.MangaInteractionHandler
|
||||
@@ -19,6 +21,7 @@ import ca.gosyer.ui.base.vm.ViewModel
|
||||
import ca.gosyer.util.lang.throwIfCancellation
|
||||
import ca.gosyer.util.lang.withIOContext
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -35,6 +38,7 @@ class MangaMenuViewModel @Inject constructor(
|
||||
private val params: Params,
|
||||
private val mangaHandler: MangaInteractionHandler,
|
||||
private val chapterHandler: ChapterInteractionHandler,
|
||||
private val categoryHandler: CategoryInteractionHandler,
|
||||
private val libraryHandler: LibraryInteractionHandler,
|
||||
private val downloadService: DownloadService,
|
||||
serverPreferences: ServerPreferences,
|
||||
@@ -53,6 +57,11 @@ class MangaMenuViewModel @Inject constructor(
|
||||
private val _isLoading = MutableStateFlow(true)
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
|
||||
private val _categoriesExist = MutableStateFlow(true)
|
||||
val categoriesExist = _categoriesExist.asStateFlow()
|
||||
|
||||
val chooseCategoriesFlow = MutableSharedFlow<Pair<List<Category>, List<Category>>>()
|
||||
|
||||
val dateTimeFormatter = uiPreferences.dateFormat().changes()
|
||||
.map {
|
||||
getDateFormat(it)
|
||||
@@ -79,6 +88,10 @@ class MangaMenuViewModel @Inject constructor(
|
||||
refreshMangaAsync(params.mangaId).await() to refreshChaptersAsync(params.mangaId).await()
|
||||
_isLoading.value = false
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
_categoriesExist.value = categoryHandler.getCategories(true).isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadManga() {
|
||||
@@ -105,6 +118,16 @@ class MangaMenuViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun setCategories() {
|
||||
scope.launch {
|
||||
manga.value?.let { manga ->
|
||||
val categories = async { categoryHandler.getCategories(true) }
|
||||
val oldCategories = async { categoryHandler.getMangaCategories(manga) }
|
||||
chooseCategoriesFlow.emit(categories.await() to oldCategories.await())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshMangaAsync(mangaId: Long, refresh: Boolean = false) = withIOContext {
|
||||
async {
|
||||
try {
|
||||
@@ -127,14 +150,36 @@ class MangaMenuViewModel @Inject constructor(
|
||||
|
||||
fun toggleFavorite() {
|
||||
scope.launch {
|
||||
manga.value?.let {
|
||||
if (it.inLibrary) {
|
||||
libraryHandler.removeMangaFromLibrary(it)
|
||||
manga.value?.let { manga ->
|
||||
if (manga.inLibrary) {
|
||||
libraryHandler.removeMangaFromLibrary(manga)
|
||||
refreshMangaAsync(manga.id).await()
|
||||
} else {
|
||||
libraryHandler.addMangaToLibrary(it)
|
||||
val categories = categoryHandler.getCategories(true)
|
||||
if (categories.isEmpty()) {
|
||||
addFavorite(emptyList(), emptyList())
|
||||
} else {
|
||||
chooseCategoriesFlow.emit(categories to emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshMangaAsync(it.id).await()
|
||||
fun addFavorite(categories: List<Category>, oldCategories: List<Category>) {
|
||||
scope.launch {
|
||||
manga.value?.let { manga ->
|
||||
if (manga.inLibrary) {
|
||||
oldCategories.filterNot { it in categories }.forEach {
|
||||
categoryHandler.removeMangaFromCategory(manga, it)
|
||||
}
|
||||
} else {
|
||||
libraryHandler.addMangaToLibrary(manga)
|
||||
}
|
||||
categories.filterNot { it in oldCategories }.forEach {
|
||||
categoryHandler.addMangaToCategory(manga, it)
|
||||
}
|
||||
refreshMangaAsync(manga.id).await()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<string name="page_progress">Page %1$d</string>
|
||||
<string name="no_chapters_found">No chapters found</string>
|
||||
<string name="failed_manga_fetch">Failed to load manga</string>
|
||||
<string name="edit_categories">Edit categories</string>
|
||||
|
||||
<!-- Sources Menu -->
|
||||
<string name="sources_home">Home</string>
|
||||
|
||||
Reference in New Issue
Block a user