Add button to reorder categories alphabetically (#9369)
Closes #6459 Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
parent
8568d5d6c3
commit
77ebc362f6
6 changed files with 106 additions and 3 deletions
|
@ -7,13 +7,22 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.ArrowBack
|
||||||
|
import androidx.compose.material.icons.outlined.SortByAlpha
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||||
import eu.kanade.presentation.category.components.CategoryListItem
|
import eu.kanade.presentation.category.components.CategoryListItem
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
|
@ -27,6 +36,7 @@ import tachiyomi.presentation.core.util.plus
|
||||||
fun CategoryScreen(
|
fun CategoryScreen(
|
||||||
state: CategoryScreenState.Success,
|
state: CategoryScreenState.Success,
|
||||||
onClickCreate: () -> Unit,
|
onClickCreate: () -> Unit,
|
||||||
|
onClickSortAlphabetically: () -> Unit,
|
||||||
onClickRename: (Category) -> Unit,
|
onClickRename: (Category) -> Unit,
|
||||||
onClickDelete: (Category) -> Unit,
|
onClickDelete: (Category) -> Unit,
|
||||||
onClickMoveUp: (Category) -> Unit,
|
onClickMoveUp: (Category) -> Unit,
|
||||||
|
@ -36,9 +46,32 @@ fun CategoryScreen(
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
AppBar(
|
TopAppBar(
|
||||||
title = stringResource(R.string.action_edit_categories),
|
title = {
|
||||||
navigateUp = navigateUp,
|
Text(
|
||||||
|
text = stringResource(R.string.action_edit_categories),
|
||||||
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = navigateUp) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.ArrowBack,
|
||||||
|
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
AppBarActions(
|
||||||
|
listOf(
|
||||||
|
AppBar.Action(
|
||||||
|
title = stringResource(R.string.action_sort),
|
||||||
|
icon = Icons.Outlined.SortByAlpha,
|
||||||
|
onClick = onClickSortAlphabetically,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -180,6 +180,35 @@ fun CategoryDeleteDialog(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CategorySortAlphabeticallyDialog(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onSort: () -> Unit,
|
||||||
|
) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
onSort()
|
||||||
|
onDismissRequest()
|
||||||
|
}) {
|
||||||
|
Text(text = stringResource(R.string.action_ok))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismissRequest) {
|
||||||
|
Text(text = stringResource(R.string.action_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.action_sort_category))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.sort_category_confirmation))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeCategoryDialog(
|
fun ChangeCategoryDialog(
|
||||||
initialSelection: List<CheckboxState<Category>>,
|
initialSelection: List<CheckboxState<Category>>,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import eu.kanade.presentation.category.CategoryScreen
|
||||||
import eu.kanade.presentation.category.components.CategoryCreateDialog
|
import eu.kanade.presentation.category.components.CategoryCreateDialog
|
||||||
import eu.kanade.presentation.category.components.CategoryDeleteDialog
|
import eu.kanade.presentation.category.components.CategoryDeleteDialog
|
||||||
import eu.kanade.presentation.category.components.CategoryRenameDialog
|
import eu.kanade.presentation.category.components.CategoryRenameDialog
|
||||||
|
import eu.kanade.presentation.category.components.CategorySortAlphabeticallyDialog
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
@ -37,6 +38,7 @@ class CategoryScreen : Screen() {
|
||||||
CategoryScreen(
|
CategoryScreen(
|
||||||
state = successState,
|
state = successState,
|
||||||
onClickCreate = { screenModel.showDialog(CategoryDialog.Create) },
|
onClickCreate = { screenModel.showDialog(CategoryDialog.Create) },
|
||||||
|
onClickSortAlphabetically = { screenModel.showDialog(CategoryDialog.SortAlphabetically) },
|
||||||
onClickRename = { screenModel.showDialog(CategoryDialog.Rename(it)) },
|
onClickRename = { screenModel.showDialog(CategoryDialog.Rename(it)) },
|
||||||
onClickDelete = { screenModel.showDialog(CategoryDialog.Delete(it)) },
|
onClickDelete = { screenModel.showDialog(CategoryDialog.Delete(it)) },
|
||||||
onClickMoveUp = screenModel::moveUp,
|
onClickMoveUp = screenModel::moveUp,
|
||||||
|
@ -68,6 +70,12 @@ class CategoryScreen : Screen() {
|
||||||
category = dialog.category,
|
category = dialog.category,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is CategoryDialog.SortAlphabetically -> {
|
||||||
|
CategorySortAlphabeticallyDialog(
|
||||||
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
|
onSort = { screenModel.sortAlphabetically() },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
|
|
@ -61,6 +61,15 @@ class CategoryScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sortAlphabetically() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
when (reorderCategory.sortAlphabetically()) {
|
||||||
|
is ReorderCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun moveUp(category: Category) {
|
fun moveUp(category: Category) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
when (reorderCategory.moveUp(category)) {
|
when (reorderCategory.moveUp(category)) {
|
||||||
|
@ -109,6 +118,7 @@ class CategoryScreenModel(
|
||||||
|
|
||||||
sealed interface CategoryDialog {
|
sealed interface CategoryDialog {
|
||||||
data object Create : CategoryDialog
|
data object Create : CategoryDialog
|
||||||
|
data object SortAlphabetically : CategoryDialog
|
||||||
data class Rename(val category: Category) : CategoryDialog
|
data class Rename(val category: Category) : CategoryDialog
|
||||||
data class Delete(val category: Category) : CategoryDialog
|
data class Delete(val category: Category) : CategoryDialog
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,27 @@ class ReorderCategory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun sortAlphabetically() = withNonCancellableContext {
|
||||||
|
mutex.withLock {
|
||||||
|
val updates = categoryRepository.getAll()
|
||||||
|
.sortedBy { category -> category.name }
|
||||||
|
.mapIndexed { index, category ->
|
||||||
|
CategoryUpdate(
|
||||||
|
id = category.id,
|
||||||
|
order = index.toLong(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
categoryRepository.updatePartial(updates)
|
||||||
|
Result.Success
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e)
|
||||||
|
Result.InternalError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed interface Result {
|
sealed interface Result {
|
||||||
data object Success : Result
|
data object Success : Result
|
||||||
data object Unchanged : Result
|
data object Unchanged : Result
|
||||||
|
|
|
@ -91,6 +91,8 @@
|
||||||
<string name="action_move_category">Set categories</string>
|
<string name="action_move_category">Set categories</string>
|
||||||
<string name="delete_category_confirmation">Do you wish to delete the category \"%s\"?</string>
|
<string name="delete_category_confirmation">Do you wish to delete the category \"%s\"?</string>
|
||||||
<string name="delete_category">Delete category</string>
|
<string name="delete_category">Delete category</string>
|
||||||
|
<string name="action_sort_category">Sort categories</string>
|
||||||
|
<string name="sort_category_confirmation">Would you like to sort the categories alphabetically?</string>
|
||||||
<string name="action_edit_cover">Edit cover</string>
|
<string name="action_edit_cover">Edit cover</string>
|
||||||
<string name="action_view_chapters">View chapters</string>
|
<string name="action_view_chapters">View chapters</string>
|
||||||
<string name="action_pause">Pause</string>
|
<string name="action_pause">Pause</string>
|
||||||
|
|
Reference in a new issue