Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drag-Drop reordering category #1427

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ dependencies {
implementation(libs.swipe)
implementation(libs.compose.webview)
implementation(libs.compose.grid)
implementation(libs.reorderable)

// Logging
implementation(libs.logcat)
Expand Down
60 changes: 39 additions & 21 deletions app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,26 @@ import androidx.compose.foundation.layout.PaddingValues
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.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryListItem
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
Expand All @@ -34,8 +41,7 @@ fun CategoryScreen(
onClickSortAlphabetically: () -> Unit,
onClickRename: (Category) -> Unit,
onClickDelete: (Category) -> Unit,
onClickMoveUp: (Category) -> Unit,
onClickMoveDown: (Category) -> Unit,
changeOrder: (Category, Int) -> Unit,
navigateUp: () -> Unit,
) {
val lazyListState = rememberLazyListState()
Expand Down Expand Up @@ -81,8 +87,7 @@ fun CategoryScreen(
PaddingValues(horizontal = MaterialTheme.padding.medium),
onClickRename = onClickRename,
onClickDelete = onClickDelete,
onMoveUp = onClickMoveUp,
onMoveDown = onClickMoveDown,
changeOrder = changeOrder,
)
}
}
Expand All @@ -94,28 +99,41 @@ private fun CategoryContent(
paddingValues: PaddingValues,
onClickRename: (Category) -> Unit,
onClickDelete: (Category) -> Unit,
onMoveUp: (Category) -> Unit,
onMoveDown: (Category) -> Unit,
changeOrder: (Category, Int) -> Unit,
) {
var reorderableList by remember { mutableStateOf(categories.toList()) }
val reorderableLazyColumnState = rememberReorderableLazyListState(lazyListState) { from, to ->
reorderableList = reorderableList.toMutableList().apply {
changeOrder(reorderableList[from.index], to.index - from.index)
add(to.index, removeAt(from.index))
}
}

LaunchedEffect(categories) {
if (!reorderableLazyColumnState.isAnyItemDragging) {
reorderableList = categories.toList()
}
}

LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
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 = reorderableList,
key = { category -> category.key },
) { category ->
ReorderableItem(reorderableLazyColumnState, category.key) {
CategoryListItem(
modifier = Modifier.animateItem(),
category = category,
onRename = { onClickRename(category) },
onDelete = { onClickDelete(category) },
)
}
}
}
}

private val Category.key get() = "category-$id"
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -19,18 +16,15 @@ 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,
Expand All @@ -42,34 +36,25 @@ fun CategoryListItem(
modifier = Modifier
.fillMaxWidth()
.clickable { 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),
.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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
changeOrder = screenModel::changeOrder,
navigateUp = navigator::pop,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,9 @@ class CategoryScreenModel(
}
}

fun moveUp(category: Category) {
fun changeOrder(category: Category, newOrder: 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, newOrder)) {
is ReorderCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
else -> {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,14 @@ import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.category.repository.CategoryRepository
import java.util.Collections

class ReorderCategory(
private val categoryRepository: CategoryRepository,
) {

private val mutex = Mutex()

suspend fun moveUp(category: Category): Result = await(category, MoveTo.UP)

suspend fun moveDown(category: Category): Result = await(category, MoveTo.DOWN)

private suspend fun await(category: Category, moveTo: MoveTo) = withNonCancellableContext {
suspend fun changeOrder(category: Category, newOrder: Int) = withNonCancellableContext {
mutex.withLock {
val categories = categoryRepository.getAll()
.filterNot(Category::isSystemCategory)
Expand All @@ -31,13 +26,10 @@ class ReorderCategory(
return@withNonCancellableContext Result.Unchanged
}

val newPosition = when (moveTo) {
MoveTo.UP -> currentIndex - 1
MoveTo.DOWN -> currentIndex + 1
}.toInt()
val newPosition = currentIndex + newOrder

try {
Collections.swap(categories, currentIndex, newPosition)
categories.add(newPosition, categories.removeAt(currentIndex))

val updates = categories.mapIndexed { index, category ->
CategoryUpdate(
Expand Down Expand Up @@ -81,9 +73,4 @@ class ReorderCategory(
data object Unchanged : Result
data class InternalError(val error: Throwable) : Result
}

private enum class MoveTo {
UP,
DOWN,
}
}
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ compose-materialmotion = "io.github.fornewid:material-motion-compose-core:2.0.1"
compose-webview = "io.github.kevinnzou:compose-webview:0.33.6"
compose-grid = "io.woong.compose.grid:grid:1.2.2"
compose-stablemarker = "com.github.skydoves:compose-stable-marker:1.0.5"
reorderable = { module = "sh.calvin.reorderable:reorderable", version = "2.4.0" }

swipe = "me.saket.swipe:swipe:1.3.0"

Expand Down