Add button to reorder categories alphabetically (#9369)

Closes #6459

Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
Pauline 2023-10-09 00:55:15 +02:00 committed by GitHub
parent 8568d5d6c3
commit 77ebc362f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 3 deletions

View file

@ -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,
) )
}, },

View file

@ -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>>,

View file

@ -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) {

View file

@ -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
} }

View file

@ -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

View file

@ -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>