Use Compose for Category screen (#7454)

* Use Compose for Category screen

* Use correct string for CategoryRenameDialog title

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 86bacbe586)

# Conflicts:
#	app/src/main/java/eu/kanade/data/category/CategoryRepositoryImpl.kt
#	app/src/main/java/eu/kanade/domain/category/interactor/InsertCategory.kt
#	app/src/main/java/eu/kanade/domain/category/repository/CategoryRepository.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryAdapter.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryHolder.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryItem.kt
#	app/src/main/res/layout/categories_item.xml
This commit is contained in:
Andreas
2022-07-09 18:31:14 +02:00
committed by Jobobby04
parent 6651ba7211
commit 4e29fd5b2a
79 changed files with 1767 additions and 2739 deletions
@@ -0,0 +1,113 @@
package eu.kanade.presentation.category
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.category.components.CategoryContent
import eu.kanade.presentation.category.components.CategoryCreateDialog
import eu.kanade.presentation.category.components.CategoryDeleteDialog
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryRenameDialog
import eu.kanade.presentation.category.components.CategoryTopAppBar
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topPaddingValues
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.category.CategoryPresenter
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Dialog
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
@Composable
fun CategoryScreen(
presenter: CategoryPresenter,
navigateUp: () -> Unit,
) {
val lazyListState = rememberLazyListState()
val topAppBarScrollState = rememberTopAppBarScrollState()
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
Scaffold(
modifier = Modifier
.statusBarsPadding()
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
topBar = {
CategoryTopAppBar(
topAppBarScrollBehavior = topAppBarScrollBehavior,
navigateUp = navigateUp,
title = stringResource(id = R.string.action_edit_categories),
)
},
floatingActionButton = {
CategoryFloatingActionButton(
lazyListState = lazyListState,
onCreate = { presenter.dialog = CategoryPresenter.Dialog.Create },
)
},
) { paddingValues ->
val context = LocalContext.current
val categories by presenter.categories.collectAsState(initial = emptyList())
if (categories.isEmpty()) {
EmptyScreen(textResource = R.string.information_empty_category)
} else {
CategoryContent(
categories = categories,
lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
onMoveUp = { presenter.moveUp(it) },
onMoveDown = { presenter.moveDown(it) },
onRename = { presenter.dialog = Dialog.Rename(it) },
onDelete = { presenter.dialog = Dialog.Delete(it) },
)
}
val onDismissRequest = { presenter.dialog = null }
when (val dialog = presenter.dialog) {
Dialog.Create -> {
CategoryCreateDialog(
onDismissRequest = onDismissRequest,
onCreate = { presenter.createCategory(it) },
title = stringResource(R.string.action_add_category),
)
}
is Dialog.Rename -> {
CategoryRenameDialog(
onDismissRequest = onDismissRequest,
onRename = { presenter.renameCategory(dialog.category, it) },
category = dialog.category.name,
)
}
is Dialog.Delete -> {
CategoryDeleteDialog(
onDismissRequest = onDismissRequest,
onDelete = { presenter.deleteCategory(dialog.category) },
title = stringResource(R.string.delete_category),
text = stringResource(R.string.delete_category_confirmation, dialog.category.name),
)
}
else -> {}
}
LaunchedEffect(Unit) {
presenter.events.collectLatest { event ->
when (event) {
is CategoryPresenter.Event.CategoryWithNameAlreadyExists -> {
context.toast(R.string.error_category_exists)
}
is CategoryPresenter.Event.InternalError -> {
context.toast(R.string.internal_error)
}
}
}
}
}
}
@@ -0,0 +1,105 @@
package eu.kanade.presentation.category
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.category.components.CategoryCreateDialog
import eu.kanade.presentation.category.components.CategoryDeleteDialog
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryTopAppBar
import eu.kanade.presentation.category.components.genre.SortTagContent
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topPaddingValues
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.category.genre.SortTagPresenter
import eu.kanade.tachiyomi.ui.category.genre.SortTagPresenter.Dialog
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
@Composable
fun SortTagScreen(
presenter: SortTagPresenter,
navigateUp: () -> Unit,
) {
val lazyListState = rememberLazyListState()
val topAppBarScrollState = rememberTopAppBarScrollState()
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
Scaffold(
modifier = Modifier
.statusBarsPadding()
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
topBar = {
CategoryTopAppBar(
topAppBarScrollBehavior = topAppBarScrollBehavior,
navigateUp = navigateUp,
title = stringResource(id = R.string.action_edit_tags),
)
},
floatingActionButton = {
CategoryFloatingActionButton(
lazyListState = lazyListState,
onCreate = { presenter.dialog = Dialog.Create },
)
},
) { paddingValues ->
val context = LocalContext.current
val tags by presenter.tags.collectAsState(initial = emptyList())
if (tags.isEmpty()) {
EmptyScreen(textResource = R.string.information_empty_tags)
} else {
SortTagContent(
categories = tags,
lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
onMoveUp = { tag, index -> presenter.moveUp(tag, index) },
onMoveDown = { tag, index -> presenter.moveDown(tag, index) },
onDelete = { presenter.dialog = Dialog.Delete(it) },
)
}
val onDismissRequest = { presenter.dialog = null }
when (val dialog = presenter.dialog) {
Dialog.Create -> {
CategoryCreateDialog(
onDismissRequest = onDismissRequest,
onCreate = { presenter.createTag(it) },
title = stringResource(R.string.add_tag),
extraMessage = stringResource(R.string.action_add_tags_message),
)
}
is Dialog.Delete -> {
CategoryDeleteDialog(
onDismissRequest = onDismissRequest,
onDelete = { presenter.delete(dialog.tag) },
title = stringResource(R.string.delete_tag),
text = stringResource(R.string.delete_tag_confirmation, dialog.tag),
)
}
else -> {}
}
LaunchedEffect(Unit) {
presenter.events.collectLatest { event ->
when (event) {
is SortTagPresenter.Event.TagExists -> {
context.toast(R.string.error_tag_exists)
}
is SortTagPresenter.Event.InternalError -> {
context.toast(R.string.internal_error)
}
}
}
}
}
}
@@ -0,0 +1,114 @@
package eu.kanade.presentation.category
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.category.components.CategoryCreateDialog
import eu.kanade.presentation.category.components.CategoryDeleteDialog
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryRenameDialog
import eu.kanade.presentation.category.components.CategoryTopAppBar
import eu.kanade.presentation.category.components.sources.SourceCategoryContent
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topPaddingValues
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryPresenter
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryPresenter.Dialog
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
@Composable
fun SourceCategoryScreen(
presenter: SourceCategoryPresenter,
navigateUp: () -> Unit,
) {
val lazyListState = rememberLazyListState()
val topAppBarScrollState = rememberTopAppBarScrollState()
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
Scaffold(
modifier = Modifier
.statusBarsPadding()
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
topBar = {
CategoryTopAppBar(
topAppBarScrollBehavior = topAppBarScrollBehavior,
navigateUp = navigateUp,
title = stringResource(id = R.string.action_edit_categories),
)
},
floatingActionButton = {
CategoryFloatingActionButton(
lazyListState = lazyListState,
onCreate = { presenter.dialog = Dialog.Create },
)
},
) { paddingValues ->
val context = LocalContext.current
val categories by presenter.categories.collectAsState(initial = emptyList())
if (categories.isEmpty()) {
EmptyScreen(textResource = R.string.information_empty_category)
} else {
SourceCategoryContent(
categories = categories,
lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
onRename = { presenter.dialog = Dialog.Rename(it) },
onDelete = { presenter.dialog = Dialog.Delete(it) },
)
}
val onDismissRequest = { presenter.dialog = null }
when (val dialog = presenter.dialog) {
Dialog.Create -> {
CategoryCreateDialog(
onDismissRequest = onDismissRequest,
onCreate = { presenter.createCategory(it) },
title = stringResource(R.string.action_add_category),
)
}
is Dialog.Rename -> {
CategoryRenameDialog(
onDismissRequest = onDismissRequest,
onRename = { presenter.renameCategory(dialog.category, it) },
category = dialog.category,
)
}
is Dialog.Delete -> {
CategoryDeleteDialog(
onDismissRequest = onDismissRequest,
onDelete = { presenter.deleteCategory(dialog.category) },
title = stringResource(R.string.delete_category),
text = stringResource(R.string.delete_category_confirmation, dialog.category),
)
}
else -> {}
}
LaunchedEffect(Unit) {
presenter.events.collectLatest { event ->
when (event) {
is SourceCategoryPresenter.Event.CategoryExists -> {
context.toast(R.string.error_category_exists)
}
is SourceCategoryPresenter.Event.InternalError -> {
context.toast(R.string.internal_error)
}
SourceCategoryPresenter.Event.InvalidName -> {
context.toast(R.string.invalid_category_name)
}
}
}
}
}
}
@@ -0,0 +1,106 @@
package eu.kanade.presentation.category
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.category.components.CategoryCreateDialog
import eu.kanade.presentation.category.components.CategoryDeleteDialog
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryTopAppBar
import eu.kanade.presentation.category.components.repo.SourceRepoContent
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topPaddingValues
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.category.repos.RepoPresenter
import eu.kanade.tachiyomi.ui.category.repos.RepoPresenter.Dialog
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
@Composable
fun SourceRepoScreen(
presenter: RepoPresenter,
navigateUp: () -> Unit,
) {
val lazyListState = rememberLazyListState()
val topAppBarScrollState = rememberTopAppBarScrollState()
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
Scaffold(
modifier = Modifier
.statusBarsPadding()
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
topBar = {
CategoryTopAppBar(
topAppBarScrollBehavior = topAppBarScrollBehavior,
navigateUp = navigateUp,
title = stringResource(R.string.action_edit_repos),
)
},
floatingActionButton = {
CategoryFloatingActionButton(
lazyListState = lazyListState,
onCreate = { presenter.dialog = Dialog.Create },
)
},
) { paddingValues ->
val context = LocalContext.current
val repos by presenter.repos.collectAsState(initial = emptyList())
if (repos.isEmpty()) {
EmptyScreen(textResource = R.string.information_empty_repos)
} else {
SourceRepoContent(
repos = repos,
lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
onDelete = { presenter.dialog = Dialog.Delete(it) },
)
}
val onDismissRequest = { presenter.dialog = null }
when (val dialog = presenter.dialog) {
Dialog.Create -> {
CategoryCreateDialog(
onDismissRequest = onDismissRequest,
onCreate = { presenter.createRepo(it) },
title = stringResource(R.string.action_add_repo),
extraMessage = stringResource(R.string.action_add_repo_message),
)
}
is Dialog.Delete -> {
CategoryDeleteDialog(
onDismissRequest = onDismissRequest,
onDelete = { presenter.deleteRepos(listOf(dialog.repo)) },
title = stringResource(R.string.delete_repo),
text = stringResource(R.string.delete_repo_confirmation, dialog.repo),
)
}
else -> {}
}
LaunchedEffect(Unit) {
presenter.events.collectLatest { event ->
when (event) {
is RepoPresenter.Event.RepoExists -> {
context.toast(R.string.error_repo_exists)
}
is RepoPresenter.Event.InternalError -> {
context.toast(R.string.internal_error)
}
is RepoPresenter.Event.InvalidName -> {
context.toast(R.string.invalid_repo_name)
}
}
}
}
}
}
@@ -0,0 +1,39 @@
package eu.kanade.presentation.category.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import eu.kanade.domain.category.model.Category
@Composable
fun CategoryContent(
categories: List<Category>,
lazyListState: LazyListState,
paddingValues: PaddingValues,
onMoveUp: (Category) -> Unit,
onMoveDown: (Category) -> Unit,
onRename: (Category) -> Unit,
onDelete: (Category) -> Unit,
) {
LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
itemsIndexed(categories) { index, category ->
CategoryListItem(
category = category,
canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex,
onMoveUp = onMoveUp,
onMoveDown = onMoveDown,
onRename = onRename,
onDelete = onDelete,
)
}
}
}
@@ -0,0 +1,129 @@
package eu.kanade.presentation.category.components
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.TextButton
import eu.kanade.tachiyomi.R
@Composable
fun CategoryCreateDialog(
onDismissRequest: () -> Unit,
onCreate: (String) -> Unit,
// SY -->
title: String,
extraMessage: String? = null,
// SY <--
) {
val (name, onNameChange) = remember { mutableStateOf("") }
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = {
onCreate(name)
onDismissRequest()
},) {
Text(text = stringResource(id = R.string.action_add))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(id = R.string.action_cancel))
}
},
title = {
Text(text = title)
},
text = {
// SY -->
Column {
if (extraMessage != null) {
Text(extraMessage)
}
// SY <--
OutlinedTextField(
value = name,
onValueChange = onNameChange,
label = {
Text(text = stringResource(id = R.string.name))
},
)
// SY -->
}
// SY <--
},
)
}
@Composable
fun CategoryRenameDialog(
onDismissRequest: () -> Unit,
onRename: (String) -> Unit,
category: String,
) {
val (name, onNameChange) = remember { mutableStateOf(category) }
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = {
onRename(name)
onDismissRequest()
},) {
Text(text = stringResource(id = android.R.string.ok))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(id = R.string.action_cancel))
}
},
title = {
Text(text = stringResource(id = R.string.action_rename_category))
},
text = {
OutlinedTextField(
value = name,
onValueChange = onNameChange,
label = {
Text(text = stringResource(id = R.string.name))
},
)
},
)
}
@Composable
fun CategoryDeleteDialog(
onDismissRequest: () -> Unit,
onDelete: () -> Unit,
title: String,
text: String,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.no))
}
},
dismissButton = {
TextButton(onClick = {
onDelete()
onDismissRequest()
},) {
Text(text = stringResource(R.string.yes))
}
},
title = {
Text(text = title)
},
text = {
Text(text = text)
},
)
}
@@ -0,0 +1,30 @@
package eu.kanade.presentation.category.components
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.ExtendedFloatingActionButton
import eu.kanade.presentation.util.isScrolledToEnd
import eu.kanade.presentation.util.isScrollingUp
import eu.kanade.tachiyomi.R
@Composable
fun CategoryFloatingActionButton(
lazyListState: LazyListState,
onCreate: () -> Unit,
) {
ExtendedFloatingActionButton(
text = { Text(text = stringResource(id = R.string.action_add)) },
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = "") },
onClick = onCreate,
modifier = Modifier
.navigationBarsPadding(),
expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(),
)
}
@@ -0,0 +1,63 @@
package eu.kanade.presentation.category.components
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
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.Edit
import androidx.compose.material.icons.outlined.Label
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.util.horizontalPadding
@Composable
fun CategoryListItem(
category: Category,
canMoveUp: Boolean,
canMoveDown: Boolean,
onMoveUp: (Category) -> Unit,
onMoveDown: (Category) -> Unit,
onRename: (Category) -> Unit,
onDelete: (Category) -> Unit,
) {
ElevatedCard {
Row(
modifier = Modifier
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
Text(text = category.name, modifier = Modifier.padding(start = horizontalPadding))
}
Row {
IconButton(
onClick = { onMoveUp(category) },
enabled = canMoveUp,
) {
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = "")
}
IconButton(
onClick = { onMoveDown(category) },
enabled = canMoveDown,
) {
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
}
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { onRename(category) }) {
Icon(imageVector = Icons.Outlined.Edit, contentDescription = "")
}
IconButton(onClick = { onDelete(category) }) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
}
}
}
}
@@ -0,0 +1,34 @@
package eu.kanade.presentation.category.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import eu.kanade.tachiyomi.R
@Composable
fun CategoryTopAppBar(
topAppBarScrollBehavior: TopAppBarScrollBehavior,
navigateUp: () -> Unit,
title: String,
) {
SmallTopAppBar(
navigationIcon = {
IconButton(onClick = navigateUp) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = stringResource(R.string.abc_action_bar_up_description),
)
}
},
title = {
Text(text = title)
},
scrollBehavior = topAppBarScrollBehavior,
)
}
@@ -0,0 +1,37 @@
package eu.kanade.presentation.category.components.genre
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
@Composable
fun SortTagContent(
categories: List<String>,
lazyListState: LazyListState,
paddingValues: PaddingValues,
onMoveUp: (String, Int) -> Unit,
onMoveDown: (String, Int) -> Unit,
onDelete: (String) -> Unit,
) {
LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
itemsIndexed(categories) { index, tag ->
SortTagListItem(
tag = tag,
index = index,
canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex,
onMoveUp = onMoveUp,
onMoveDown = onMoveDown,
onDelete = onDelete,
)
}
}
}
@@ -0,0 +1,58 @@
package eu.kanade.presentation.category.components.genre
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
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.Label
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import eu.kanade.presentation.util.horizontalPadding
@Composable
fun SortTagListItem(
tag: String,
index: Int,
canMoveUp: Boolean,
canMoveDown: Boolean,
onMoveUp: (String, Int) -> Unit,
onMoveDown: (String, Int) -> Unit,
onDelete: (String) -> Unit,
) {
ElevatedCard {
Row(
modifier = Modifier
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
Text(text = tag, modifier = Modifier.padding(start = horizontalPadding))
}
Row {
IconButton(
onClick = { onMoveUp(tag, index) },
enabled = canMoveUp,
) {
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = "")
}
IconButton(
onClick = { onMoveDown(tag, index) },
enabled = canMoveDown,
) {
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
}
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { onDelete(tag) }) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
}
}
}
}
@@ -0,0 +1,30 @@
package eu.kanade.presentation.category.components.repo
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
@Composable
fun SourceRepoContent(
repos: List<String>,
lazyListState: LazyListState,
paddingValues: PaddingValues,
onDelete: (String) -> Unit,
) {
LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
items(repos) { repo ->
SourceRepoListItem(
repo = repo,
onDelete = onDelete,
)
}
}
}
@@ -0,0 +1,39 @@
package eu.kanade.presentation.category.components.repo
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Label
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import eu.kanade.presentation.util.horizontalPadding
@Composable
fun SourceRepoListItem(
repo: String,
onDelete: (String) -> Unit,
) {
ElevatedCard {
Row(
modifier = Modifier
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
Text(text = repo, modifier = Modifier.padding(start = horizontalPadding))
}
Row {
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { onDelete(repo) }) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
}
}
}
}
@@ -0,0 +1,32 @@
package eu.kanade.presentation.category.components.sources
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
@Composable
fun SourceCategoryContent(
categories: List<String>,
lazyListState: LazyListState,
paddingValues: PaddingValues,
onRename: (String) -> Unit,
onDelete: (String) -> Unit,
) {
LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
itemsIndexed(categories) { index, category ->
SourceCategoryListItem(
category = category,
onRename = onRename,
onDelete = onDelete,
)
}
}
}
@@ -0,0 +1,44 @@
package eu.kanade.presentation.category.components.sources
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Label
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import eu.kanade.presentation.util.horizontalPadding
@Composable
fun SourceCategoryListItem(
category: String,
onRename: (String) -> Unit,
onDelete: (String) -> Unit,
) {
ElevatedCard {
Row(
modifier = Modifier
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
Text(text = category, modifier = Modifier.padding(start = horizontalPadding))
}
Row {
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { onRename(category) }) {
Icon(imageVector = Icons.Outlined.Edit, contentDescription = "")
}
IconButton(onClick = { onDelete(category) }) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
}
}
}
}
@@ -60,8 +60,8 @@ fun ChapterDownloadIndicator(
},
) {
val indicatorModifier = Modifier
.size(IndicatorSize)
.padding(IndicatorPadding)
.size(IndicatorSize)
.padding(IndicatorPadding)
if (isDownloaded) {
Icon(
imageVector = Icons.Default.CheckCircle,
@@ -151,5 +151,6 @@ fun ChapterDownloadIndicator(
private val IndicatorSize = 26.dp
private val IndicatorPadding = 2.dp
// To match composable parameter name when used later
private val IndicatorStrokeWidth = IndicatorPadding
@@ -3,6 +3,12 @@ package eu.kanade.presentation.util
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.dp
val horizontalPadding = 16.dp
private val horizontal = 16.dp
val topPaddingValues = PaddingValues(top = 8.dp)
private val vertical = 8.dp
val horizontalPadding = horizontal
val verticalPadding = vertical
val topPaddingValues = PaddingValues(top = vertical)