mirror of
https://github.com/mihonapp/mihon.git
synced 2025-06-28 20:17:51 +02:00
Add back support for drag-and-drop category reordering (#1427)
This commit is contained in:
@ -267,6 +267,7 @@ dependencies {
|
||||
implementation(libs.swipe)
|
||||
implementation(libs.compose.webview)
|
||||
implementation(libs.compose.grid)
|
||||
implementation(libs.reorderable)
|
||||
|
||||
// Logging
|
||||
implementation(libs.logcat)
|
||||
|
@ -2,15 +2,19 @@ package eu.kanade.presentation.category
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.SortByAlpha
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||
import eu.kanade.presentation.category.components.CategoryListItem
|
||||
@ -18,6 +22,8 @@ import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
@ -34,8 +40,7 @@ fun CategoryScreen(
|
||||
onClickSortAlphabetically: () -> Unit,
|
||||
onClickRename: (Category) -> Unit,
|
||||
onClickDelete: (Category) -> Unit,
|
||||
onClickMoveUp: (Category) -> Unit,
|
||||
onClickMoveDown: (Category) -> Unit,
|
||||
onChangeOrder: (Category, Int) -> Unit,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
@ -76,13 +81,10 @@ fun CategoryScreen(
|
||||
CategoryContent(
|
||||
categories = state.categories,
|
||||
lazyListState = lazyListState,
|
||||
paddingValues = paddingValues +
|
||||
topSmallPaddingValues +
|
||||
PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||
paddingValues = paddingValues,
|
||||
onClickRename = onClickRename,
|
||||
onClickDelete = onClickDelete,
|
||||
onMoveUp = onClickMoveUp,
|
||||
onMoveDown = onClickMoveDown,
|
||||
onChangeOrder = onChangeOrder,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -94,28 +96,44 @@ private fun CategoryContent(
|
||||
paddingValues: PaddingValues,
|
||||
onClickRename: (Category) -> Unit,
|
||||
onClickDelete: (Category) -> Unit,
|
||||
onMoveUp: (Category) -> Unit,
|
||||
onMoveDown: (Category) -> Unit,
|
||||
onChangeOrder: (Category, Int) -> Unit,
|
||||
) {
|
||||
val categoriesState = remember { categories.toMutableStateList() }
|
||||
val reorderableState = rememberReorderableLazyListState(lazyListState, paddingValues) { from, to ->
|
||||
val item = categoriesState.removeAt(from.index)
|
||||
categoriesState.add(to.index, item)
|
||||
onChangeOrder(item, to.index)
|
||||
}
|
||||
|
||||
LaunchedEffect(categories) {
|
||||
if (!reorderableState.isAnyItemDragging) {
|
||||
categoriesState.clear()
|
||||
categoriesState.addAll(categories)
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = lazyListState,
|
||||
contentPadding = paddingValues,
|
||||
contentPadding = paddingValues +
|
||||
topSmallPaddingValues +
|
||||
PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = categories,
|
||||
key = { _, category -> "category-${category.id}" },
|
||||
) { index, category ->
|
||||
CategoryListItem(
|
||||
modifier = Modifier.animateItem(),
|
||||
category = category,
|
||||
canMoveUp = index != 0,
|
||||
canMoveDown = index != categories.lastIndex,
|
||||
onMoveUp = onMoveUp,
|
||||
onMoveDown = onMoveDown,
|
||||
onRename = { onClickRename(category) },
|
||||
onDelete = { onClickDelete(category) },
|
||||
)
|
||||
items(
|
||||
items = categoriesState,
|
||||
key = { category -> category.key },
|
||||
) { category ->
|
||||
ReorderableItem(reorderableState, category.key) {
|
||||
CategoryListItem(
|
||||
modifier = Modifier.animateItem(),
|
||||
category = category,
|
||||
onRename = { onClickRename(category) },
|
||||
onDelete = { onClickDelete(category) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val Category.key inline get() = "category-$id"
|
||||
|
@ -2,14 +2,11 @@ package eu.kanade.presentation.category.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
import androidx.compose.material.icons.outlined.ArrowDropDown
|
||||
import androidx.compose.material.icons.outlined.ArrowDropUp
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.DragHandle
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
@ -19,57 +16,42 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import sh.calvin.reorderable.ReorderableCollectionItemScope
|
||||
import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
||||
@Composable
|
||||
fun CategoryListItem(
|
||||
fun ReorderableCollectionItemScope.CategoryListItem(
|
||||
category: Category,
|
||||
canMoveUp: Boolean,
|
||||
canMoveDown: Boolean,
|
||||
onMoveUp: (Category) -> Unit,
|
||||
onMoveDown: (Category) -> Unit,
|
||||
onRename: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ElevatedCard(
|
||||
modifier = modifier,
|
||||
) {
|
||||
ElevatedCard(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onRename() }
|
||||
.clickable(onClick = onRename)
|
||||
.padding(vertical = MaterialTheme.padding.small)
|
||||
.padding(
|
||||
start = MaterialTheme.padding.medium,
|
||||
top = MaterialTheme.padding.medium,
|
||||
start = MaterialTheme.padding.small,
|
||||
end = MaterialTheme.padding.medium,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null)
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.DragHandle,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(MaterialTheme.padding.medium)
|
||||
.draggableHandle(),
|
||||
)
|
||||
Text(
|
||||
text = category.name,
|
||||
modifier = Modifier
|
||||
.padding(start = MaterialTheme.padding.medium),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
Row {
|
||||
IconButton(
|
||||
onClick = { onMoveUp(category) },
|
||||
enabled = canMoveUp,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = null)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { onMoveDown(category) },
|
||||
enabled = canMoveDown,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = null)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = onRename) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Edit,
|
||||
@ -77,7 +59,10 @@ fun CategoryListItem(
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(MR.strings.action_delete))
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = stringResource(MR.strings.action_delete),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,8 +43,7 @@ class CategoryScreen : Screen() {
|
||||
onClickSortAlphabetically = { screenModel.showDialog(CategoryDialog.SortAlphabetically) },
|
||||
onClickRename = { screenModel.showDialog(CategoryDialog.Rename(it)) },
|
||||
onClickDelete = { screenModel.showDialog(CategoryDialog.Delete(it)) },
|
||||
onClickMoveUp = screenModel::moveUp,
|
||||
onClickMoveDown = screenModel::moveDown,
|
||||
onChangeOrder = screenModel::changeOrder,
|
||||
navigateUp = navigator::pop,
|
||||
)
|
||||
|
||||
|
@ -74,18 +74,9 @@ class CategoryScreenModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun moveUp(category: Category) {
|
||||
fun changeOrder(category: Category, newIndex: Int) {
|
||||
screenModelScope.launch {
|
||||
when (reorderCategory.moveUp(category)) {
|
||||
is ReorderCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun moveDown(category: Category) {
|
||||
screenModelScope.launch {
|
||||
when (reorderCategory.moveDown(category)) {
|
||||
when (reorderCategory.changeOrder(category, newIndex)) {
|
||||
is ReorderCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||
else -> {}
|
||||
}
|
||||
|
Reference in New Issue
Block a user